chromium/chrome/browser/ash/printing/oauth2/authorization_zones_manager.h

// 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.

#ifndef CHROME_BROWSER_ASH_PRINTING_OAUTH2_AUTHORIZATION_ZONES_MANAGER_H_
#define CHROME_BROWSER_ASH_PRINTING_OAUTH2_AUTHORIZATION_ZONES_MANAGER_H_

#include <memory>
#include <string>

#include "base/functional/callback.h"
#include "chrome/browser/ash/printing/oauth2/status_code.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/sync/model/data_type_store.h"
#include "components/sync/model/data_type_sync_bridge.h"

class GURL;
class Profile;

namespace chromeos {
class Uri;
}  // namespace chromeos

namespace syncer {
class DataTypeLocalChangeProcessor;
}  // namespace syncer

namespace ash::printing::oauth2 {

class AuthorizationZone;
class ClientIdsDatabase;

// This class is responsible for managing OAuth2 sessions required to get access
// to some printers. In the API provided by the class, printers are referred to
// as IPP Endpoints. IPP Endpoints that require OAuth2 token report the
// following IPP attributes in the response for Get-Printer-Attributes request:
//   * oauth-authorization-server-uri - the URL of the Authorization Server;
//   * oauth-authorization-scope - optional, if missing use an empty string.
// These two values correspond to the parameters `auth_server` and `scope` in
// the API below.
//
// How to use:
//  * SaveAuthorizationServerAsTrusted() - this must be called once for each
//    Authorization Server to mark it as trusted. The list of trusted
//    Authorization Servers is saved in user's profile. All API calls for any
//    Authorization Server not included in the trusted list will fail with the
//    error StatusCode::kUntrustedAuthorizationServer.
//  * InitAuthorization() - the callback returns a URL that must be opened in an
//    internet browser to allow a user to go through an authorization procedure.
//  * FinishAuthorization() - this method finalizes the authorization procedure
//    started by InitAuthorization(). The authorization procedure in the
//    internet browser is completed when the browser receives a response with
//    the HTTP 302 status code. The value of the "Location" attribute from the
//    HTTP header must be passed to this method to finalize the process and
//    open an OAuth2 session with the Authorization Server.
//  * GetEndpointAccessToken() - the callback returns an endpoint access token
//    for given IPP Endpoint. You can call this method as the first one and
//    then fallback to InitAuthorization()/FinishAuthorization() when it
//    returns the status StatusCode::kAuthorizationNeeded. This method can
//    be called repeatedly and will return the same token for the same
//    parameters until the method MarkEndpointAccessTokenAsExpired() is called.
//  * MarkEndpointAccessTokenAsExpired() - call this method to mark the
//    endpoint access token as expired. Then the following call to
//    GetEndpointAccessToken() will try to obtain a new endpoint access token
//    or returns StatusCode::kAuthorizationNeeded when a new authorization
//    procedure is needed (i.e. a new call to InitAuthorization() and
//    FinishAuthorization() is required).
//
// Results and errors are returned by StatusCallback passed as the last
// parameter. See chrome/browser/ash/printing/oauth2/status_code.h for more
// details.
class AuthorizationZonesManager : public KeyedService {
 public:
  using CreateAuthZoneCallback =
      base::RepeatingCallback<std::unique_ptr<AuthorizationZone>(
          const GURL& url,
          ClientIdsDatabase* client_ids_database)>;

  // `profile` must not be nullptr.
  static std::unique_ptr<AuthorizationZonesManager> Create(Profile* profile);
  static std::unique_ptr<AuthorizationZonesManager> CreateForTesting(
      Profile* profile,
      CreateAuthZoneCallback auth_zone_creator,
      std::unique_ptr<ClientIdsDatabase> client_ids_database,
      std::unique_ptr<syncer::DataTypeLocalChangeProcessor> change_processor,
      syncer::OnceDataTypeStoreFactory store_factory);

  ~AuthorizationZonesManager() override;
  virtual syncer::DataTypeSyncBridge* GetDataTypeSyncBridge() = 0;

  // Marks `auth_server` as trusted.
  virtual StatusCode SaveAuthorizationServerAsTrusted(
      const GURL& auth_server) = 0;

  // Starts authorization process. If successful, the `callback` is called
  // with StatusCode::kOK and with an authorization URL that must be opened in
  // an internet browser to enable the user to complete the authorization
  // process. Before calling this method the caller should make sure that
  // creating a new session is necessary by calling the method
  // GetEndpointAccessToken() first.
  virtual void InitAuthorization(const GURL& auth_server,
                                 const std::string& scope,
                                 StatusCallback callback) = 0;

  // Finalizes authorization process. As an parameter this method takes an URL
  // that the internet browser was redirected to at the end of the authorization
  // procedure completed by the user. The return code StatusCode::kOK means
  // that a new OAuth2 session was created and the caller can now use the method
  // GetEndpointAccessToken() to get an endpoint access token.
  virtual void FinishAuthorization(const GURL& auth_server,
                                   const GURL& redirect_url,
                                   StatusCallback callback) = 0;

  // Obtains an endpoint access token to use with the given IPP Endpoint. If
  // succeeded `callback` returns StatusCode::kOK and endpoint access token as
  // `data`. StatusCode::kAuthorizationNeeded means that the caller must call
  // methods InitAuthorization() and FinishAuthorization() first to open OAuth2
  // session. The parameter `scope` is used only when an endpoint access token
  // for the given IPP endpoint does not exists yet (the parameter has no
  // effects when the endpoint access token already exists).
  virtual void GetEndpointAccessToken(const GURL& auth_server,
                                      const chromeos::Uri& ipp_endpoint,
                                      const std::string& scope,
                                      StatusCallback callback) = 0;

  // This method marks the `endpoint_access_token` issued for `ipp_endpoint` as
  // expired. The next call to GetEndpointAccessToken() will start internally a
  // procedure to obtain a new endpoint access token. This method should be
  // called when the IPP Endpoint rejects a request with `endpoint_access_token`
  // by sending back a response with the HTTP 401 status code. If the HTTP
  // header of that response contains "error" attribute, you should check if it
  // equals "invalid_token" before calling this method. See RFC 6750 for more
  // details.
  virtual void MarkEndpointAccessTokenAsExpired(
      const GURL& auth_server,
      const chromeos::Uri& ipp_endpoint,
      const std::string& endpoint_access_token) = 0;

 protected:
  AuthorizationZonesManager();
};

}  // namespace ash::printing::oauth2

#endif  // CHROME_BROWSER_ASH_PRINTING_OAUTH2_AUTHORIZATION_ZONES_MANAGER_H_