// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/browser/cast_permission_manager.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "chromecast/base/metrics/cast_metrics_helper.h"
#include "chromecast/browser/cast_permission_user_data.h"
#include "chromecast/common/activity_url_filter.h"
#include "components/cast_receiver/browser/public/permissions_manager.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/web_contents.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"
namespace {
bool IsRequestingOriginAllowed(
const GURL& requesting_origin,
const GURL& app_web_url,
const std::vector<std::string>& additional_feature_permission_origins) {
// |app_web_url| is allowed by default.
if (requesting_origin == app_web_url.DeprecatedGetOriginAsURL()) {
return true;
}
chromecast::ActivityUrlFilter activity_url_filter(
additional_feature_permission_origins);
return activity_url_filter.UrlMatchesWhitelist(requesting_origin);
}
blink::mojom::PermissionStatus GetPermissionStatusFromCastPermissionUserData(
blink::PermissionType permission,
const GURL& requesting_origin,
chromecast::shell::CastPermissionUserData* cast_permission_user_data) {
std::string app_id = cast_permission_user_data->GetAppId();
GURL app_web_url = cast_permission_user_data->GetAppWebUrl();
// We expect to grant blink::PermissionType::PROTECTED_MEDIA_IDENTIFIER
// to origins same as |app_web_url| by default.
if (requesting_origin != app_web_url.DeprecatedGetOriginAsURL() ||
permission != blink::PermissionType::PROTECTED_MEDIA_IDENTIFIER) {
chromecast::metrics::CastMetricsHelper::GetInstance()
->RecordApplicationEventWithValue(
app_id, /*session_id=*/"", /*sdk_version=*/"",
"Cast.Platform.PermissionRequestWithFrame",
static_cast<int>(permission));
}
if (!cast_permission_user_data->GetEnforceFeaturePermissions()) {
return blink::mojom::PermissionStatus::GRANTED;
}
// Permissions that are granted by default should have been added to the
// FeaturePermissions in CastPermissionUserData.
bool permitted =
cast_permission_user_data->GetFeaturePermissions().count(
static_cast<int32_t>(permission)) > 0 &&
IsRequestingOriginAllowed(
requesting_origin, app_web_url,
cast_permission_user_data->GetAdditionalFeaturePermissionOrigins());
return permitted ? blink::mojom::PermissionStatus::GRANTED
: ::blink::mojom::PermissionStatus::DENIED;
}
} // namespace
namespace chromecast {
namespace shell {
// TODO(b/191718807): Be more restrictive on the permissions.
// Currently, only collect metrics.
blink::mojom::PermissionStatus GetPermissionStatusInternal(
blink::PermissionType permission,
const GURL& requesting_origin) {
// We expect to grant blink::PermissionType::BACKGROUND_SYNC by
// default.
if (permission != blink::PermissionType::BACKGROUND_SYNC) {
metrics::CastMetricsHelper::GetInstance()->RecordApplicationEventWithValue(
"Cast.Platform.PermissionRequestWithoutFrame",
static_cast<int>(permission));
}
blink::mojom::PermissionStatus permission_status =
blink::mojom::PermissionStatus::GRANTED;
LOG(INFO) << __func__ << ": "
<< (permission_status == blink::mojom::PermissionStatus::GRANTED
? " grants "
: " doesn't grant ")
<< "permission " << static_cast<int>(permission)
<< " out of frame context.";
return permission_status;
}
blink::mojom::PermissionStatus GetPermissionStatusInternal(
blink::PermissionType permission,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin) {
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host);
const cast_receiver::PermissionsManager* permissions_manager =
cast_receiver::PermissionsManager::GetInstance(*web_contents);
if (permissions_manager) {
const blink::mojom::PermissionStatus permission_status =
permissions_manager->GetPermissionStatus(permission, requesting_origin);
if (permission_status == blink::mojom::PermissionStatus::GRANTED) {
return permission_status;
}
}
DCHECK(render_frame_host);
CastPermissionUserData* cast_permission_user_data =
CastPermissionUserData::FromWebContents(web_contents);
if (!cast_permission_user_data) {
LOG(ERROR) << __func__ << ": No permission data in frame!";
return GetPermissionStatusInternal(permission, requesting_origin);
}
blink::mojom::PermissionStatus permission_status =
GetPermissionStatusFromCastPermissionUserData(
permission, requesting_origin, cast_permission_user_data);
LOG(INFO) << __func__ << ": "
<< (permission_status == blink::mojom::PermissionStatus::GRANTED
? " grants "
: " doesn't grant ")
<< "permission " << static_cast<int>(permission)
<< " to frame associated with app: "
<< cast_permission_user_data->GetAppId();
return permission_status;
}
CastPermissionManager::CastPermissionManager() {}
CastPermissionManager::~CastPermissionManager() {}
void CastPermissionManager::RequestPermissions(
content::RenderFrameHost* render_frame_host,
const content::PermissionRequestDescription& request_description,
base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus>&)>
callback) {
std::vector<blink::mojom::PermissionStatus> permission_statuses;
for (auto permission : request_description.permissions) {
permission_statuses.push_back(GetPermissionStatusInternal(
permission, render_frame_host, request_description.requesting_origin));
}
std::move(callback).Run(permission_statuses);
}
void CastPermissionManager::ResetPermission(blink::PermissionType permission,
const GURL& requesting_origin,
const GURL& embedding_origin) {}
void CastPermissionManager::RequestPermissionsFromCurrentDocument(
content::RenderFrameHost* render_frame_host,
const content::PermissionRequestDescription& request_description,
base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus>&)>
callback) {
std::vector<blink::mojom::PermissionStatus> permission_statuses;
for (auto permission : request_description.permissions) {
permission_statuses.push_back(GetPermissionStatusInternal(
permission, render_frame_host,
render_frame_host->GetLastCommittedOrigin().GetURL()));
}
std::move(callback).Run(permission_statuses);
}
blink::mojom::PermissionStatus CastPermissionManager::GetPermissionStatus(
blink::PermissionType permission,
const GURL& requesting_origin,
const GURL& embedding_origin) {
return GetPermissionStatusInternal(permission, requesting_origin);
}
content::PermissionResult
CastPermissionManager::GetPermissionResultForOriginWithoutContext(
blink::PermissionType permission,
const url::Origin& requesting_origin,
const url::Origin& embedding_origin) {
blink::mojom::PermissionStatus status = GetPermissionStatus(
permission, requesting_origin.GetURL(), embedding_origin.GetURL());
return content::PermissionResult(
status, content::PermissionStatusSource::UNSPECIFIED);
}
blink::mojom::PermissionStatus
CastPermissionManager::GetPermissionStatusForCurrentDocument(
blink::PermissionType permission,
content::RenderFrameHost* render_frame_host,
bool should_include_device_status) {
return GetPermissionStatusInternal(
permission, render_frame_host,
render_frame_host->GetLastCommittedOrigin().GetURL());
}
blink::mojom::PermissionStatus
CastPermissionManager::GetPermissionStatusForWorker(
blink::PermissionType permission,
content::RenderProcessHost* render_process_host,
const GURL& worker_origin) {
return GetPermissionStatusInternal(permission, worker_origin);
}
blink::mojom::PermissionStatus
CastPermissionManager::GetPermissionStatusForEmbeddedRequester(
blink::PermissionType permission,
content::RenderFrameHost* render_frame_host,
const url::Origin& requesting_origin) {
return GetPermissionStatusInternal(permission, render_frame_host,
requesting_origin.GetURL());
}
} // namespace shell
} // namespace chromecast