| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/extensions/api/identity/identity_api.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/lazy_instance.h" |
| #include "base/macros.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/trace_event/trace_event.h" |
| #include "base/values.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/app_mode/app_mode_utils.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/extensions/api/identity/identity_constants.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/signin/account_consistency_mode_manager.h" |
| #include "chrome/browser/signin/chrome_signin_client_factory.h" |
| #include "chrome/browser/signin/identity_manager_factory.h" |
| #include "chrome/common/extensions/api/identity.h" |
| #include "chrome/common/url_constants.h" |
| #include "extensions/browser/extension_function_dispatcher.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/extension_l10n_util.h" |
| #include "extensions/common/manifest_handlers/oauth2_manifest_handler.h" |
| #include "extensions/common/permissions/permission_set.h" |
| #include "extensions/common/permissions/permissions_data.h" |
| #include "google_apis/gaia/gaia_urls.h" |
| #include "url/gurl.h" |
| |
| namespace extensions { |
| |
| #if BUILDFLAG(ENABLE_DICE_SUPPORT) |
| const base::Feature kExtensionsAllAccountsFeature{ |
| "ExtensionsAllAccounts", base::FEATURE_DISABLED_BY_DEFAULT}; |
| #endif |
| |
| IdentityTokenCacheValue::IdentityTokenCacheValue() |
| : status_(CACHE_STATUS_NOTFOUND) {} |
| |
| IdentityTokenCacheValue::IdentityTokenCacheValue( |
| const IssueAdviceInfo& issue_advice) |
| : status_(CACHE_STATUS_ADVICE), issue_advice_(issue_advice) { |
| expiration_time_ = |
| base::Time::Now() + base::TimeDelta::FromSeconds( |
| identity_constants::kCachedIssueAdviceTTLSeconds); |
| } |
| |
| IdentityTokenCacheValue::IdentityTokenCacheValue(const std::string& token, |
| base::TimeDelta time_to_live) |
| : status_(CACHE_STATUS_TOKEN), token_(token) { |
| // Remove 20 minutes from the ttl so cached tokens will have some time |
| // to live any time they are returned. |
| time_to_live -= base::TimeDelta::FromMinutes(20); |
| |
| base::TimeDelta zero_delta; |
| if (time_to_live < zero_delta) |
| time_to_live = zero_delta; |
| |
| expiration_time_ = base::Time::Now() + time_to_live; |
| } |
| |
| IdentityTokenCacheValue::IdentityTokenCacheValue( |
| const IdentityTokenCacheValue& other) = default; |
| |
| IdentityTokenCacheValue::~IdentityTokenCacheValue() {} |
| |
| IdentityTokenCacheValue::CacheValueStatus IdentityTokenCacheValue::status() |
| const { |
| if (is_expired()) |
| return IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND; |
| else |
| return status_; |
| } |
| |
| const IssueAdviceInfo& IdentityTokenCacheValue::issue_advice() const { |
| return issue_advice_; |
| } |
| |
| const std::string& IdentityTokenCacheValue::token() const { return token_; } |
| |
| bool IdentityTokenCacheValue::is_expired() const { |
| return status_ == CACHE_STATUS_NOTFOUND || |
| expiration_time_ < base::Time::Now(); |
| } |
| |
| const base::Time& IdentityTokenCacheValue::expiration_time() const { |
| return expiration_time_; |
| } |
| |
| IdentityAPI::IdentityAPI(content::BrowserContext* context) |
| : profile_(Profile::FromBrowserContext(context)) { |
| IdentityManagerFactory::GetForProfile(profile_)->AddObserver(this); |
| } |
| |
| IdentityAPI::~IdentityAPI() {} |
| |
| IdentityMintRequestQueue* IdentityAPI::mint_queue() { return &mint_queue_; } |
| |
| void IdentityAPI::SetCachedToken(const ExtensionTokenKey& key, |
| const IdentityTokenCacheValue& token_data) { |
| auto it = token_cache_.find(key); |
| if (it != token_cache_.end() && it->second.status() <= token_data.status()) |
| token_cache_.erase(it); |
| |
| token_cache_.insert(std::make_pair(key, token_data)); |
| } |
| |
| void IdentityAPI::EraseCachedToken(const std::string& extension_id, |
| const std::string& token) { |
| CachedTokens::iterator it; |
| for (it = token_cache_.begin(); it != token_cache_.end(); ++it) { |
| if (it->first.extension_id == extension_id && |
| it->second.status() == IdentityTokenCacheValue::CACHE_STATUS_TOKEN && |
| it->second.token() == token) { |
| token_cache_.erase(it); |
| break; |
| } |
| } |
| } |
| |
| void IdentityAPI::EraseAllCachedTokens() { token_cache_.clear(); } |
| |
| const IdentityTokenCacheValue& IdentityAPI::GetCachedToken( |
| const ExtensionTokenKey& key) { |
| return token_cache_[key]; |
| } |
| |
| const IdentityAPI::CachedTokens& IdentityAPI::GetAllCachedTokens() { |
| return token_cache_; |
| } |
| |
| void IdentityAPI::Shutdown() { |
| on_shutdown_callback_list_.Notify(); |
| IdentityManagerFactory::GetForProfile(profile_)->RemoveObserver(this); |
| } |
| |
| static base::LazyInstance<BrowserContextKeyedAPIFactory<IdentityAPI>>:: |
| DestructorAtExit g_identity_api_factory = LAZY_INSTANCE_INITIALIZER; |
| |
| // static |
| BrowserContextKeyedAPIFactory<IdentityAPI>* IdentityAPI::GetFactoryInstance() { |
| return g_identity_api_factory.Pointer(); |
| } |
| |
| bool IdentityAPI::AreExtensionsRestrictedToPrimaryAccount() { |
| #if BUILDFLAG(ENABLE_DICE_SUPPORT) |
| if (!AccountConsistencyModeManager::IsDiceEnabledForProfile(profile_)) |
| return true; |
| return !base::FeatureList::IsEnabled(kExtensionsAllAccountsFeature); |
| #else |
| return true; |
| #endif |
| } |
| |
| void IdentityAPI::OnRefreshTokenUpdatedForAccount( |
| const AccountInfo& account_info) { |
| // Refresh tokens are sometimes made available in contexts where |
| // AccountTrackerService is not tracking the account in question (one example |
| // is SupervisedUserService::InitSync()). Bail out in these cases. |
| if (account_info.gaia.empty()) |
| return; |
| |
| FireOnAccountSignInChanged(account_info.gaia, true); |
| } |
| |
| void IdentityAPI::OnAccountRemovedWithInfo(const AccountInfo& account_info) { |
| DCHECK(!account_info.gaia.empty()); |
| FireOnAccountSignInChanged(account_info.gaia, false); |
| } |
| |
| void IdentityAPI::FireOnAccountSignInChanged(const std::string& gaia_id, |
| bool is_signed_in) { |
| DCHECK(!gaia_id.empty()); |
| api::identity::AccountInfo api_account_info; |
| api_account_info.id = gaia_id; |
| |
| std::unique_ptr<base::ListValue> args = |
| api::identity::OnSignInChanged::Create(api_account_info, is_signed_in); |
| std::unique_ptr<Event> event(new Event( |
| events::IDENTITY_ON_SIGN_IN_CHANGED, |
| api::identity::OnSignInChanged::kEventName, std::move(args), profile_)); |
| |
| if (on_signin_changed_callback_for_testing_) |
| on_signin_changed_callback_for_testing_.Run(event.get()); |
| |
| EventRouter::Get(profile_)->BroadcastEvent(std::move(event)); |
| } |
| |
| template <> |
| void BrowserContextKeyedAPIFactory<IdentityAPI>::DeclareFactoryDependencies() { |
| DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory()); |
| |
| DependsOn(ChromeSigninClientFactory::GetInstance()); |
| DependsOn(IdentityManagerFactory::GetInstance()); |
| } |
| |
| } // namespace extensions |