blob: 61e5858200a2c039a5eef6bcde203fc539fd236b [file] [log] [blame]
// Copyright 2020 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/device_api/device_service_impl.h"
#include <memory>
#include "base/containers/contains.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/app_mode/app_mode_utils.h"
#include "chrome/browser/device_api/device_attribute_api.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/policy/web_app_policy_constants.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "url/gurl.h"
#include "url/origin.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_data.h"
#include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_manager.h"
#include "chrome/browser/ash/login/app_mode/kiosk_launch_controller.h"
#include "components/user_manager/user_manager.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chrome/browser/lacros/app_mode/kiosk_session_service_lacros.h"
#include "components/policy/core/common/policy_loader_lacros.h"
#endif
namespace {
// Check whether the target origin is allowed to access to the device
// attributes.
bool CanAccessDeviceAttributes(const PrefService* prefs,
const url::Origin& origin) {
const base::Value::List& prefs_list =
prefs->GetList(prefs::kDeviceAttributesAllowedForOrigins);
return base::Contains(prefs_list, origin, [](const auto& entry) {
return url::Origin::Create(GURL(entry.GetString()));
});
}
// Check whether the target origin is the same as the main application running
// in the Kiosk session.
bool IsEqualToKioskOrigin(const url::Origin& origin) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
const AccountId& account_id =
user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId();
const ash::WebKioskAppData* app_data =
ash::WebKioskAppManager::Get()->GetAppByAccountId(account_id);
return url::Origin::Create(app_data->install_url()) == origin;
#elif BUILDFLAG(IS_CHROMEOS_LACROS)
DCHECK(KioskSessionServiceLacros::Get());
return url::Origin::Create(
KioskSessionServiceLacros::Get()->GetInstallURL()) == origin;
#else
return false;
#endif
}
// Check whether the target origin is included in the WebAppInstallForceList
// policy.
bool IsForceInstalledOrigin(const PrefService* prefs,
const url::Origin& origin) {
const base::Value::List& prefs_list =
prefs->GetList(prefs::kWebAppInstallForceList);
return base::Contains(prefs_list, origin, [](const auto& entry) {
std::string entry_url = entry.FindKey(web_app::kUrlKey)->GetString();
return url::Origin::Create(GURL(entry_url));
});
}
const Profile* GetProfile(content::RenderFrameHost& host) {
return Profile::FromBrowserContext(host.GetBrowserContext());
}
const PrefService* GetPrefs(content::RenderFrameHost& host) {
return GetProfile(host)->GetPrefs();
}
bool IsAffiliatedUser() {
#if BUILDFLAG(IS_CHROMEOS_ASH)
const user_manager::User* user =
user_manager::UserManager::Get()->GetPrimaryUser();
return user && user->IsAffiliated();
#elif BUILDFLAG(IS_CHROMEOS_LACROS)
return policy::PolicyLoaderLacros::IsMainUserAffiliated();
#else
return false;
#endif
}
bool IsTrustedContext(content::RenderFrameHost& host,
const url::Origin& origin) {
// TODO(anqing): This feature flag is turned on by default for origin trial.
// The flag will be removed when permission policies are ready.
if (!base::FeatureList::IsEnabled(features::kEnableRestrictedWebApis))
return false;
// Do not create the service for the incognito mode.
if (GetProfile(host)->IsIncognitoProfile())
return false;
if (chrome::IsRunningInAppMode()) {
return IsEqualToKioskOrigin(origin);
} else {
return IsForceInstalledOrigin(GetPrefs(host), origin);
}
}
} // namespace
DeviceServiceImpl::DeviceServiceImpl(
content::RenderFrameHost& host,
mojo::PendingReceiver<blink::mojom::DeviceAPIService> receiver)
: DocumentService(host, std::move(receiver)) {
pref_change_registrar_.Init(
Profile::FromBrowserContext(host.GetBrowserContext())->GetPrefs());
pref_change_registrar_.Add(
prefs::kDeviceAttributesAllowedForOrigins,
base::BindRepeating(&DeviceServiceImpl::OnDisposingIfNeeded,
base::Unretained(this)));
pref_change_registrar_.Add(
prefs::kWebAppInstallForceList,
base::BindRepeating(&DeviceServiceImpl::OnDisposingIfNeeded,
base::Unretained(this)));
}
DeviceServiceImpl::~DeviceServiceImpl() = default;
// static
void DeviceServiceImpl::Create(
content::RenderFrameHost* host,
mojo::PendingReceiver<blink::mojom::DeviceAPIService> receiver) {
CHECK(host);
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!IsTrustedContext(*host,
host->GetMainFrame()->GetLastCommittedOrigin())) {
// Not sending bad message here since the API is always exposed to the end
// user.
return;
}
// The object is bound to the lifetime of |host| and the mojo
// connection. See DocumentService for details.
new DeviceServiceImpl(*host, std::move(receiver));
}
// static
void DeviceServiceImpl::RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterListPref(prefs::kDeviceAttributesAllowedForOrigins);
}
void DeviceServiceImpl::OnDisposingIfNeeded() {
// DeviceServiceImpl is allocated on the heap, thus it is safe to remove it
// like this.
if (!IsTrustedContext(render_frame_host(), origin())) {
ResetAndDeleteThis();
}
}
void DeviceServiceImpl::GetDirectoryId(GetDirectoryIdCallback callback) {
GetDeviceAttribute(base::BindOnce(device_attribute_api::GetDirectoryId),
std::move(callback));
}
void DeviceServiceImpl::GetHostname(GetHostnameCallback callback) {
GetDeviceAttribute(base::BindOnce(device_attribute_api::GetHostname),
std::move(callback));
}
void DeviceServiceImpl::GetSerialNumber(GetSerialNumberCallback callback) {
GetDeviceAttribute(base::BindOnce(device_attribute_api::GetSerialNumber),
std::move(callback));
}
void DeviceServiceImpl::GetAnnotatedAssetId(
GetAnnotatedAssetIdCallback callback) {
GetDeviceAttribute(base::BindOnce(device_attribute_api::GetAnnotatedAssetId),
std::move(callback));
}
void DeviceServiceImpl::GetAnnotatedLocation(
GetAnnotatedLocationCallback callback) {
GetDeviceAttribute(base::BindOnce(device_attribute_api::GetAnnotatedLocation),
std::move(callback));
}
void DeviceServiceImpl::GetDeviceAttribute(
base::OnceCallback<void(DeviceAttributeCallback)> handler,
DeviceAttributeCallback callback) {
if (!IsAffiliatedUser()) {
device_attribute_api::ReportNotAffiliatedError(std::move(callback));
return;
}
if (!CanAccessDeviceAttributes(GetPrefs(render_frame_host()), origin())) {
device_attribute_api::ReportNotAllowedError(std::move(callback));
return;
}
std::move(handler).Run(std::move(callback));
}