PageRenderTime 48ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/chrome/browser/installable/digital_asset_links/digital_asset_links_handler.cc

http://github.com/chromium/chromium
C++ | 311 lines | 226 code | 49 blank | 36 comment | 32 complexity | 7ab23392f3d1f2ccc378ea6004aa3411 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.0, BSD-2-Clause, LGPL-2.1, MPL-2.0, 0BSD, EPL-1.0, MPL-2.0-no-copyleft-exception, GPL-2.0, BitTorrent-1.0, CPL-1.0, LGPL-3.0, Unlicense, BSD-3-Clause, CC0-1.0, JSON, MIT, GPL-3.0, CC-BY-SA-3.0, AGPL-1.0
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "chrome/browser/installable/digital_asset_links/digital_asset_links_handler.h"
  5. #include <vector>
  6. #include "base/bind.h"
  7. #include "base/json/json_reader.h"
  8. #include "base/logging.h"
  9. #include "base/strings/stringprintf.h"
  10. #include "base/values.h"
  11. #include "content/public/browser/web_contents.h"
  12. #include "net/base/load_flags.h"
  13. #include "net/base/url_util.h"
  14. #include "net/http/http_response_headers.h"
  15. #include "net/http/http_status_code.h"
  16. #include "net/http/http_util.h"
  17. #include "net/traffic_annotation/network_traffic_annotation.h"
  18. #include "services/network/public/cpp/resource_request.h"
  19. #include "services/network/public/cpp/shared_url_loader_factory.h"
  20. #include "services/network/public/cpp/simple_url_loader.h"
  21. #include "url/origin.h"
  22. namespace {
  23. // In some cases we get a network change while fetching the digital asset
  24. // links file. See https://crbug.com/987329.
  25. const int kNumNetworkRetries = 1;
  26. // Location on a website where the asset links file can be found, see
  27. // https://developers.google.com/digital-asset-links/v1/getting-started.
  28. const char kAssetLinksAbsolutePath[] = ".well-known/assetlinks.json";
  29. GURL GetUrlForAssetLinks(const url::Origin& origin) {
  30. return origin.GetURL().Resolve(kAssetLinksAbsolutePath);
  31. }
  32. // An example, well formed asset links file for reference:
  33. // [{
  34. // "relation": ["delegate_permission/common.handle_all_urls"],
  35. // "target": {
  36. // "namespace": "android_app",
  37. // "package_name": "com.peter.trustedpetersactivity",
  38. // "sha256_cert_fingerprints": [
  39. // "FA:2A:03: ... :9D"
  40. // ]
  41. // }
  42. // }, {
  43. // "relation": ["delegate_permission/common.handle_all_urls"],
  44. // "target": {
  45. // "namespace": "android_app",
  46. // "package_name": "com.example.firstapp",
  47. // "sha256_cert_fingerprints": [
  48. // "64:2F:D4: ... :C1"
  49. // ]
  50. // }
  51. // }]
  52. bool StatementHasMatchingRelationship(const base::Value& statement,
  53. const std::string& target_relation) {
  54. const base::Value* relations =
  55. statement.FindKeyOfType("relation", base::Value::Type::LIST);
  56. if (!relations)
  57. return false;
  58. for (const auto& relation : relations->GetList()) {
  59. if (relation.is_string() && relation.GetString() == target_relation)
  60. return true;
  61. }
  62. return false;
  63. }
  64. bool StatementHasMatchingTargetValue(const base::Value& statement,
  65. const std::string& target_key,
  66. const std::string& target_value) {
  67. const base::Value* package = statement.FindPathOfType(
  68. {"target", target_key}, base::Value::Type::STRING);
  69. return package && package->GetString() == target_value;
  70. }
  71. bool StatementHasMatchingFingerprint(const base::Value& statement,
  72. const std::string& target_fingerprint) {
  73. const base::Value* fingerprints = statement.FindPathOfType(
  74. {"target", "sha256_cert_fingerprints"}, base::Value::Type::LIST);
  75. if (!fingerprints)
  76. return false;
  77. for (const auto& fingerprint : fingerprints->GetList()) {
  78. if (fingerprint.is_string() &&
  79. fingerprint.GetString() == target_fingerprint) {
  80. return true;
  81. }
  82. }
  83. return false;
  84. }
  85. // Shows a warning message in the DevTools console.
  86. void AddMessageToConsole(content::WebContents* web_contents,
  87. const std::string& message) {
  88. if (web_contents) {
  89. web_contents->GetMainFrame()->AddMessageToConsole(
  90. blink::mojom::ConsoleMessageLevel::kWarning, message);
  91. return;
  92. }
  93. // Fallback to LOG.
  94. LOG(WARNING) << message;
  95. }
  96. } // namespace
  97. namespace digital_asset_links {
  98. const char kDigitalAssetLinksCheckResponseKeyLinked[] = "linked";
  99. DigitalAssetLinksHandler::DigitalAssetLinksHandler(
  100. scoped_refptr<network::SharedURLLoaderFactory> factory,
  101. content::WebContents* web_contents)
  102. : content::WebContentsObserver(web_contents),
  103. shared_url_loader_factory_(std::move(factory)) {}
  104. DigitalAssetLinksHandler::~DigitalAssetLinksHandler() = default;
  105. void DigitalAssetLinksHandler::OnURLLoadComplete(
  106. std::string relationship,
  107. base::Optional<std::string> fingerprint,
  108. std::map<std::string, std::string> target_values,
  109. std::unique_ptr<std::string> response_body) {
  110. int response_code = -1;
  111. if (url_loader_->ResponseInfo() && url_loader_->ResponseInfo()->headers)
  112. response_code = url_loader_->ResponseInfo()->headers->response_code();
  113. if (!response_body || response_code != net::HTTP_OK) {
  114. int net_error = url_loader_->NetError();
  115. if (net_error == net::ERR_INTERNET_DISCONNECTED ||
  116. net_error == net::ERR_NAME_NOT_RESOLVED) {
  117. AddMessageToConsole(web_contents(),
  118. "Digital Asset Links connection failed.");
  119. std::move(callback_).Run(RelationshipCheckResult::kNoConnection);
  120. return;
  121. }
  122. AddMessageToConsole(
  123. web_contents(),
  124. base::StringPrintf(
  125. "Digital Asset Links endpoint responded with code %d.",
  126. response_code));
  127. std::move(callback_).Run(RelationshipCheckResult::kFailure);
  128. return;
  129. }
  130. data_decoder::DataDecoder::ParseJsonIsolated(
  131. *response_body,
  132. base::BindOnce(&DigitalAssetLinksHandler::OnJSONParseResult,
  133. weak_ptr_factory_.GetWeakPtr(), std::move(relationship),
  134. std::move(fingerprint), std::move(target_values)));
  135. url_loader_.reset(nullptr);
  136. }
  137. void DigitalAssetLinksHandler::OnJSONParseResult(
  138. std::string relationship,
  139. base::Optional<std::string> fingerprint,
  140. std::map<std::string, std::string> target_values,
  141. data_decoder::DataDecoder::ValueOrError result) {
  142. if (!result.value) {
  143. AddMessageToConsole(
  144. web_contents(),
  145. "Digital Asset Links response parsing failed with message: " +
  146. *result.error);
  147. std::move(callback_).Run(RelationshipCheckResult::kFailure);
  148. return;
  149. }
  150. auto& statement_list = *result.value;
  151. if (!statement_list.is_list()) {
  152. std::move(callback_).Run(RelationshipCheckResult::kFailure);
  153. AddMessageToConsole(web_contents(), "Statement List is not a list.");
  154. return;
  155. }
  156. // We only output individual statement failures if none match.
  157. std::vector<std::string> failures;
  158. for (const auto& statement : statement_list.GetList()) {
  159. if (!statement.is_dict()) {
  160. failures.push_back("Statement is not a dictionary.");
  161. continue;
  162. }
  163. if (!StatementHasMatchingRelationship(statement, relationship)) {
  164. failures.push_back("Statement failure matching relationship.");
  165. continue;
  166. }
  167. if (fingerprint &&
  168. !StatementHasMatchingFingerprint(statement, *fingerprint)) {
  169. failures.push_back("Statement failure matching fingerprint.");
  170. continue;
  171. }
  172. bool failed_target_check = false;
  173. for (const auto& key_value : target_values) {
  174. if (!StatementHasMatchingTargetValue(statement, key_value.first,
  175. key_value.second)) {
  176. failures.push_back("Statement failure matching " + key_value.first +
  177. ".");
  178. failed_target_check = true;
  179. break;
  180. }
  181. }
  182. if (failed_target_check)
  183. continue;
  184. std::move(callback_).Run(RelationshipCheckResult::kSuccess);
  185. return;
  186. }
  187. for (const auto& failure_reason : failures)
  188. AddMessageToConsole(web_contents(), failure_reason);
  189. std::move(callback_).Run(RelationshipCheckResult::kFailure);
  190. }
  191. bool DigitalAssetLinksHandler::CheckDigitalAssetLinkRelationshipForAndroidApp(
  192. const std::string& web_domain,
  193. const std::string& relationship,
  194. const std::string& fingerprint,
  195. const std::string& package,
  196. RelationshipCheckResultCallback callback) {
  197. // TODO(rayankans): Should we also check the namespace here?
  198. return CheckDigitalAssetLinkRelationship(
  199. web_domain, relationship, fingerprint, {{"package_name", package}},
  200. std::move(callback));
  201. }
  202. bool DigitalAssetLinksHandler::CheckDigitalAssetLinkRelationshipForWebApk(
  203. const std::string& web_domain,
  204. const std::string& manifest_url,
  205. RelationshipCheckResultCallback callback) {
  206. return CheckDigitalAssetLinkRelationship(
  207. web_domain, "delegate_permission/common.query_webapk", base::nullopt,
  208. {{"namespace", "web"}, {"site", manifest_url}}, std::move(callback));
  209. }
  210. bool DigitalAssetLinksHandler::CheckDigitalAssetLinkRelationship(
  211. const std::string& web_domain,
  212. const std::string& relationship,
  213. const base::Optional<std::string>& fingerprint,
  214. const std::map<std::string, std::string>& target_values,
  215. RelationshipCheckResultCallback callback) {
  216. // TODO(peconn): Propagate the use of url::Origin backwards to clients.
  217. GURL request_url = GetUrlForAssetLinks(url::Origin::Create(GURL(web_domain)));
  218. if (!request_url.is_valid())
  219. return false;
  220. // Resetting both the callback and SimpleURLLoader here to ensure
  221. // that any previous requests will never get a
  222. // OnURLLoadComplete. This effectively cancels any checks that was
  223. // done over this handler.
  224. callback_ = std::move(callback);
  225. net::NetworkTrafficAnnotationTag traffic_annotation =
  226. net::DefineNetworkTrafficAnnotation("digital_asset_links", R"(
  227. semantics {
  228. sender: "Digital Asset Links Handler"
  229. description:
  230. "Digital Asset Links APIs allows any caller to check pre declared"
  231. "relationships between two assets which can be either web domains"
  232. "or native applications. This requests checks for a specific "
  233. "relationship declared by a web site with an Android application"
  234. trigger:
  235. "When the related application makes a claim to have the queried"
  236. "relationship with the web domain"
  237. destination: WEBSITE
  238. }
  239. policy {
  240. cookies_allowed: YES
  241. cookies_store: "user"
  242. setting: "Not user controlled. But the verification is a trusted API"
  243. "that doesn't use user data"
  244. policy_exception_justification:
  245. "Not implemented, considered not useful as no content is being "
  246. "uploaded; this request merely downloads the resources on the web."
  247. })");
  248. auto request = std::make_unique<network::ResourceRequest>();
  249. request->url = request_url;
  250. url_loader_ =
  251. network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
  252. url_loader_->SetRetryOptions(
  253. kNumNetworkRetries,
  254. network::SimpleURLLoader::RetryMode::RETRY_ON_NETWORK_CHANGE);
  255. url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
  256. shared_url_loader_factory_.get(),
  257. base::BindOnce(&DigitalAssetLinksHandler::OnURLLoadComplete,
  258. weak_ptr_factory_.GetWeakPtr(), relationship, fingerprint,
  259. target_values));
  260. return true;
  261. }
  262. } // namespace digital_asset_links