Allow user subset exclusion for first-party origin trial tokens
Previously, we added support to the token format to specify the usage restriction to be applied. This |usage| field was only supported for third-party tokens, for scope reasons (as described in the bug). Now, there's a use case for specifying the usage restriction on first-party tokens. This CL adds support for all tokens to have the |usage| field. That mostly means removing the logic to limit the field to third-party tokens. Bug: 1151330 Change-Id: I2fdc27a030e49a8b8c83a695a496c1263678c06c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2560560 Commit-Queue: Jason Chase <chasej@chromium.org> Reviewed-by: Ian Clelland <iclelland@chromium.org> Cr-Commit-Position: refs/heads/master@{#831381}
This commit is contained in:
docs
third_party/blink
common
origin_trials
web_tests
http
tests
origin_trials
tools/origin_trials
@ -7,6 +7,11 @@ changes required.
|
||||
|
||||
## Code Changes
|
||||
|
||||
NOTE: You can land these code changes before requesting to run an origin trial.
|
||||
These code changes make it possible to control a feature via an origin trial,
|
||||
but don't require an origin trial to be approved. For more on the process, see
|
||||
[Running an Origin Trial].
|
||||
|
||||
### Runtime Enabled Features
|
||||
|
||||
First, you’ll need to configure [runtime\_enabled\_features.json5]. This is
|
||||
@ -186,6 +191,14 @@ To test an origin trial feature during development, follow these steps:
|
||||
tools/origin_trials/generate_token.py http://localhost:8000 MyFeature
|
||||
```
|
||||
|
||||
There are additional flags to generate third-party tokens, set the expiry
|
||||
date, and control other options. See the command help for details (`--help`).
|
||||
For example, to generate a third-party token, with [user subset exclusion]:
|
||||
|
||||
```
|
||||
tools/origin_trials/generate_token.py --is-third-party --usage-restriction=subset http://localhost:8000 MyFeature
|
||||
```
|
||||
|
||||
2. Copy the token from the end of the output and use it in a `<meta>` tag or
|
||||
an `Origin-Trial` header as described in the [Developer Guide].
|
||||
|
||||
@ -236,4 +249,5 @@ as tests for script-added tokens. For examples, refer to the existing tests in
|
||||
[css\_properties.json5]: /third_party/blink/renderer/core/css/css_properties.json5
|
||||
[origin-trial-test-property]: https://chromium.googlesource.com/chromium/src/+/ff2ab8b89745602c8300322c2a0158e210178c7e/third_party/blink/renderer/core/css/css_properties.json5#2635
|
||||
[CSSStyleDeclaration]: /third_party/blink/renderer/core/css/css_style_declaration.idl
|
||||
|
||||
[Running an Origin Trial]: https://www.chromium.org/blink/origin-trials/running-an-origin-trial
|
||||
[user subset exclusion]: https://docs.google.com/document/d/1xALH9W7rWmX0FpjudhDeS2TNTEOXuPn4Tlc9VmuPdHA/edit#heading=h.myaz1twlipw
|
||||
|
@ -238,13 +238,10 @@ std::unique_ptr<TrialToken> TrialToken::Parse(const std::string& token_payload,
|
||||
is_third_party = is_third_party_value->GetBool();
|
||||
}
|
||||
|
||||
// The |usage| field is optional and can only be set if |isThirdParty| flag
|
||||
// is true. If found, ensure its value is either empty or "subset".
|
||||
// The |usage| field is optional. If found, ensure its value is either empty
|
||||
// or "subset".
|
||||
std::string* usage_value = datadict->FindStringKey("usage");
|
||||
if (usage_value) {
|
||||
if (!is_third_party) {
|
||||
return nullptr;
|
||||
}
|
||||
if (usage_value->empty()) {
|
||||
usage = UsageRestriction::kNone;
|
||||
} else if (*usage_value == kUsageSubset) {
|
||||
|
@ -263,6 +263,14 @@ const char kSampleSubdomainTokenJSON[] =
|
||||
"{\"origin\": \"https://example.com:443\", \"isSubdomain\": true, "
|
||||
"\"feature\": \"Frobulate\", \"expiry\": 1458766277}";
|
||||
|
||||
const char kUsageEmptyTokenJSON[] =
|
||||
"{\"origin\": \"https://valid.example.com:443\", \"usage\": \"\", "
|
||||
"\"feature\": \"Frobulate\", \"expiry\": 1458766277}";
|
||||
|
||||
const char kUsageSubsetTokenJSON[] =
|
||||
"{\"origin\": \"https://valid.example.com:443\", \"usage\": \"subset\", "
|
||||
"\"feature\": \"Frobulate\", \"expiry\": 1458766277}";
|
||||
|
||||
const char kSampleNonThirdPartyTokenJSON[] =
|
||||
"{\"origin\": \"https://valid.example.com:443\", \"isThirdParty\": false, "
|
||||
"\"feature\": \"Frobulate\", \"expiry\": 1458766277}";
|
||||
@ -327,10 +335,6 @@ const char* kInvalidTokensVersion3[] = {
|
||||
"{\"origin\": \"https://a.a\", \"isThirdParty\": true, \"usage\": "
|
||||
"\"cycle\", \"feature\": \"a\", "
|
||||
"\"expiry\": 1458766277}",
|
||||
// usage in non third party token
|
||||
"{\"origin\": \"https://a.a\", \"isThirdParty\": false, \"usage\": "
|
||||
"\"subset\", \"feature\": \"a\", "
|
||||
"\"expiry\": 1458766277}",
|
||||
};
|
||||
|
||||
// Valid token JSON. The feature name matches matches kExpectedLongFeatureName
|
||||
@ -994,6 +998,7 @@ TEST_P(TrialTokenParseTest, ParseValidToken) {
|
||||
EXPECT_FALSE(token->match_subdomains());
|
||||
EXPECT_EQ(expected_origin_, token->origin());
|
||||
EXPECT_EQ(expected_expiry_, token->expiry_time());
|
||||
EXPECT_EQ(TrialToken::UsageRestriction::kNone, token->usage_restriction());
|
||||
}
|
||||
|
||||
TEST_P(TrialTokenParseTest, ParseValidNonSubdomainToken) {
|
||||
@ -1135,6 +1140,26 @@ TEST_F(TrialTokenTest, ParseValidThirdPartyTokenInvalidVersion) {
|
||||
EXPECT_EQ(expected_expiry_, token->expiry_time());
|
||||
}
|
||||
|
||||
TEST_F(TrialTokenTest, ParseValidUsageEmptyToken) {
|
||||
std::unique_ptr<TrialToken> token = Parse(kUsageEmptyTokenJSON, kVersion3);
|
||||
ASSERT_TRUE(token);
|
||||
EXPECT_EQ(kExpectedFeatureName, token->feature_name());
|
||||
EXPECT_FALSE(token->is_third_party());
|
||||
EXPECT_EQ(TrialToken::UsageRestriction::kNone, token->usage_restriction());
|
||||
EXPECT_EQ(expected_origin_, token->origin());
|
||||
EXPECT_EQ(expected_expiry_, token->expiry_time());
|
||||
}
|
||||
|
||||
TEST_F(TrialTokenTest, ParseValidUsageSubsetToken) {
|
||||
std::unique_ptr<TrialToken> token = Parse(kUsageSubsetTokenJSON, kVersion3);
|
||||
ASSERT_TRUE(token);
|
||||
EXPECT_EQ(kExpectedFeatureName, token->feature_name());
|
||||
EXPECT_FALSE(token->is_third_party());
|
||||
EXPECT_EQ(TrialToken::UsageRestriction::kSubset, token->usage_restriction());
|
||||
EXPECT_EQ(expected_origin_, token->origin());
|
||||
EXPECT_EQ(expected_expiry_, token->expiry_time());
|
||||
}
|
||||
|
||||
TEST_F(TrialTokenTest, ParseValidThirdPartyUsageSubsetToken) {
|
||||
std::unique_ptr<TrialToken> token =
|
||||
Parse(kSampleThirdPartyTokenUsageSubsetJSON, kVersion3);
|
||||
|
@ -107,8 +107,7 @@ TrialTokenResult TrialTokenValidator::ValidateToken(
|
||||
if (policy->IsTokenDisabled(trial_token->signature()))
|
||||
return TrialTokenResult(OriginTrialTokenStatus::kTokenDisabled);
|
||||
|
||||
if (trial_token->is_third_party() &&
|
||||
trial_token->usage_restriction() ==
|
||||
if (trial_token->usage_restriction() ==
|
||||
TrialToken::UsageRestriction::kSubset &&
|
||||
policy->IsFeatureDisabledForUser(trial_token->feature_name()))
|
||||
return TrialTokenResult(OriginTrialTokenStatus::kFeatureDisabledForUser);
|
||||
|
@ -167,6 +167,17 @@ const char kThirdPartyUsageSubsetToken[] =
|
||||
"InN1YnNldCIsICJmZWF0dXJlIjogIkZyb2J1bGF0ZVRoaXJkUGFydHkiLCAiZXhw"
|
||||
"aXJ5IjogMjAwMDAwMDAwMH0=";
|
||||
|
||||
// Well-formed token, for first party, with usage set to user subset exclusion.
|
||||
// Generate this token with the command (in tools/origin_trials):
|
||||
// generate_token.py valid.example.com FrobulateThirdParty
|
||||
// --version 3 --usage-restriction subset --expire-timestamp=2000000000
|
||||
const char kUsageSubsetToken[] =
|
||||
"Axi0wjIp8gaGr/"
|
||||
"pTPzwrHqeWXnmhCiZhE2edsJ9fHX25GV6A8zg1fCv27qhBNnbxjqDpU0a+"
|
||||
"xKScEiqKK1MS3QUAAAB2eyJvcmlnaW4iOiAiaHR0cHM6Ly92YWxpZC5leGFtcGxlLmNvbTo0ND"
|
||||
"MiLCAidXNhZ2UiOiAic3Vic2V0IiwgImZlYXR1cmUiOiAiRnJvYnVsYXRlVGhpcmRQYXJ0eSIs"
|
||||
"ICJleHBpcnkiOiAyMDAwMDAwMDAwfQ==";
|
||||
|
||||
// This timestamp is set to a time after the expiry timestamp of kExpiredToken,
|
||||
// but before the expiry timestamp of kValidToken.
|
||||
double kNowTimestamp = 1500000000;
|
||||
@ -372,8 +383,24 @@ TEST_F(TrialTokenValidatorTest, ValidatorRespectsDisabledFeatures) {
|
||||
validator_.ValidateToken(kSampleToken, appropriate_origin_, Now())
|
||||
.status);
|
||||
}
|
||||
TEST_F(TrialTokenValidatorTest,
|
||||
ValidatorRespectsDisabledFeaturesForUserWithFirstPartyToken) {
|
||||
// Token should be valid if the feature is not disabled for user.
|
||||
TrialTokenResult result =
|
||||
validator_.ValidateToken(kUsageSubsetToken, appropriate_origin_, Now());
|
||||
EXPECT_EQ(blink::OriginTrialTokenStatus::kSuccess, result.status);
|
||||
EXPECT_EQ(kAppropriateThirdPartyFeatureName, result.feature_name);
|
||||
EXPECT_EQ(kSampleTokenExpiryTime, result.expiry_time);
|
||||
// Token should be invalid when the feature is disabled for user.
|
||||
DisableFeatureForUser(kAppropriateThirdPartyFeatureName);
|
||||
EXPECT_EQ(
|
||||
blink::OriginTrialTokenStatus::kFeatureDisabledForUser,
|
||||
validator_.ValidateToken(kUsageSubsetToken, appropriate_origin_, Now())
|
||||
.status);
|
||||
}
|
||||
|
||||
TEST_F(TrialTokenValidatorTest, ValidatorRespectsDisabledFeaturesForUser) {
|
||||
TEST_F(TrialTokenValidatorTest,
|
||||
ValidatorRespectsDisabledFeaturesForUserWithThirdPartyToken) {
|
||||
// Token should be valid if the feature is not disabled for user.
|
||||
TrialTokenResult result = validator_.ValidateToken(
|
||||
kThirdPartyUsageSubsetToken, inappropriate_origin_, &appropriate_origin_,
|
||||
|
16
third_party/blink/web_tests/http/tests/origin_trials/third-party-first-party-token-in-markup-enabled.html
vendored
Normal file
16
third_party/blink/web_tests/http/tests/origin_trials/third-party-first-party-token-in-markup-enabled.html
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Test that trial which supports third-party is enabled by valid first-party token provided in markup</title>
|
||||
<!-- Generate this token with the command:
|
||||
generate_token.py http://127.0.0.1:8000 FrobulateThirdParty --expire-timestamp=2000000000
|
||||
-->
|
||||
<meta http-equiv="origin-trial"
|
||||
content="A0TyTDFs2N+ecIcAeQo8DlipLQwIEcD+bJlRGpZj5NfDDGF8VEcEL4zByhPrdadxF1PX8VG4bfd2XZep1O6m3wsAAABbeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiRnJvYnVsYXRlVGhpcmRQYXJ0eSIsICJleHBpcnkiOiAyMDAwMDAwMDAwfQ==" />
|
||||
<script src="../resources/testharness.js"></script>
|
||||
<script src="../resources/testharnessreport.js"></script>
|
||||
<script src="resources/origintrials.js"></script>
|
||||
<script>
|
||||
// The trial should be enabled, as first party tokens can be used normally,
|
||||
// when the trial has third-party support enabled.
|
||||
expect_success_third_party();
|
||||
</script>
|
@ -222,9 +222,6 @@ def main():
|
||||
if (usage_restriction is not None and version != VERSION3):
|
||||
print("The usage field can only be be set in Version 3 token.")
|
||||
sys.exit(1)
|
||||
if (usage_restriction is not None and not is_third_party):
|
||||
print("Only third party token supports alternative usage restriction.")
|
||||
sys.exit(1)
|
||||
if (usage_restriction not in USAGE_RESTRICTION):
|
||||
print("Only empty string and \"subset\" are supported in the usage field.")
|
||||
sys.exit(1)
|
||||
|
@ -226,9 +226,6 @@ def main():
|
||||
if (args.version[0] != 3):
|
||||
print("Only version 3 token supports alternative usage restriction.")
|
||||
sys.exit(1)
|
||||
if (not args.is_third_party):
|
||||
print("Only third party token supports alternative usage restriction.")
|
||||
sys.exit(1)
|
||||
if (args.usage_restriction not in USAGE_RESTRICTION):
|
||||
print(
|
||||
"Only empty string and \"subset\" are supported in alternative usage "
|
||||
|
Reference in New Issue
Block a user