Bug 1861819 - Use JS::ParseJSONWithHandler in BasePrincipal. r=peterv
This avoid intermediate object tree for "JSON to principal" case. Differential Revision: https://phabricator.services.mozilla.com/D192146
This commit is contained in:
@@ -44,9 +44,15 @@
|
|||||||
#include "nsIURIMutator.h"
|
#include "nsIURIMutator.h"
|
||||||
#include "mozilla/PermissionManager.h"
|
#include "mozilla/PermissionManager.h"
|
||||||
|
|
||||||
#include "json/json.h"
|
|
||||||
#include "nsSerializationHelper.h"
|
#include "nsSerializationHelper.h"
|
||||||
|
|
||||||
|
#include "js/JSON.h"
|
||||||
|
#include "ContentPrincipalJSONHandler.h"
|
||||||
|
#include "ExpandedPrincipalJSONHandler.h"
|
||||||
|
#include "NullPrincipalJSONHandler.h"
|
||||||
|
#include "PrincipalJSONHandler.h"
|
||||||
|
#include "SubsumedPrincipalJSONHandler.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
BasePrincipal::BasePrincipal(PrincipalKind aKind,
|
BasePrincipal::BasePrincipal(PrincipalKind aKind,
|
||||||
@@ -136,113 +142,183 @@ BasePrincipal::GetSiteOriginNoSuffix(nsACString& aSiteOrigin) {
|
|||||||
return GetOriginNoSuffix(aSiteOrigin);
|
return GetOriginNoSuffix(aSiteOrigin);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the inner Json::value of the serialized principal
|
template <typename HandlerTypesT>
|
||||||
// Example input and return values:
|
bool ContainerPrincipalJSONHandler<HandlerTypesT>::ProcessInnerResult(
|
||||||
// Null principal:
|
bool aResult) {
|
||||||
// {"0":{"0":"moz-nullprincipal:{56cac540-864d-47e7-8e25-1614eab5155e}"}} ->
|
if (!aResult) {
|
||||||
// {"0":"moz-nullprincipal:{56cac540-864d-47e7-8e25-1614eab5155e}"}
|
NS_WARNING("Failed to parse inner object");
|
||||||
//
|
mState = State::Error;
|
||||||
// Content principal:
|
return false;
|
||||||
// {"1":{"0":"https://mozilla.com"}} -> {"0":"https://mozilla.com"}
|
|
||||||
//
|
|
||||||
// Expanded principal:
|
|
||||||
// {"2":{"0":"<base64principal1>,<base64principal2>"}} ->
|
|
||||||
// {"0":"<base64principal1>,<base64principal2>"}
|
|
||||||
//
|
|
||||||
// System principal:
|
|
||||||
// {"3":{}} -> {}
|
|
||||||
// The aKey passed in also returns the corresponding PrincipalKind enum
|
|
||||||
//
|
|
||||||
// Warning: The Json::Value* pointer is into the aRoot object
|
|
||||||
static const Json::Value* GetPrincipalObject(const Json::Value& aRoot,
|
|
||||||
int& aOutPrincipalKind) {
|
|
||||||
const Json::Value::Members members = aRoot.getMemberNames();
|
|
||||||
// We only support one top level key in the object
|
|
||||||
if (members.size() != 1) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
// members[0] here is the "0", "1", "2", "3" principalKind
|
return true;
|
||||||
// that is the top level of the serialized JSON principal
|
|
||||||
const std::string stringPrincipalKind = members[0];
|
|
||||||
|
|
||||||
// Next we take the string value from the JSON
|
|
||||||
// and convert it into the int for the BasePrincipal::PrincipalKind enum
|
|
||||||
|
|
||||||
// Verify that the key is within the valid range
|
|
||||||
int principalKind = std::stoi(stringPrincipalKind);
|
|
||||||
MOZ_ASSERT(BasePrincipal::eNullPrincipal == 0,
|
|
||||||
"We need to rely on 0 being a bounds check for the first "
|
|
||||||
"principal kind.");
|
|
||||||
if (principalKind < 0 || principalKind > BasePrincipal::eKindMax) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
MOZ_ASSERT(principalKind == BasePrincipal::eNullPrincipal ||
|
|
||||||
principalKind == BasePrincipal::eContentPrincipal ||
|
|
||||||
principalKind == BasePrincipal::eExpandedPrincipal ||
|
|
||||||
principalKind == BasePrincipal::eSystemPrincipal);
|
|
||||||
aOutPrincipalKind = principalKind;
|
|
||||||
|
|
||||||
if (!aRoot[stringPrincipalKind].isObject()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the inner value of the principal object
|
|
||||||
return &aRoot[stringPrincipalKind];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accepts the JSON inner object without the wrapping principalKind
|
template <typename HandlerTypesT>
|
||||||
// (See GetPrincipalObject for the inner object response examples)
|
bool ContainerPrincipalJSONHandler<HandlerTypesT>::startObject() {
|
||||||
// Creates an array of KeyVal objects that are all defined on the principal
|
if (mInnerHandler.isSome()) {
|
||||||
// Each principal type (null, content, expanded) has a KeyVal that stores the
|
return CallOnInner([&](auto& aInner) { return aInner.startObject(); });
|
||||||
// fields of the JSON
|
}
|
||||||
//
|
|
||||||
// This simplifies deserializing elsewhere as we do the checking for presence
|
switch (mState) {
|
||||||
// and string values here for the complete set of serializable keys that the
|
case State::Init:
|
||||||
// corresponding principal supports.
|
mState = State::StartObject;
|
||||||
//
|
break;
|
||||||
// The KeyVal object has the following fields:
|
case State::SystemPrincipal_Key:
|
||||||
// - valueWasSerialized: is true if the deserialized JSON contained a string
|
mState = State::SystemPrincipal_StartObject;
|
||||||
// value
|
break;
|
||||||
// - value: The string that was serialized for this key
|
default:
|
||||||
// - key: an SerializableKeys enum value specific to the principal.
|
NS_WARNING("Unexpected object value");
|
||||||
// For example content principal is an enum of: eURI, eDomain,
|
mState = State::Error;
|
||||||
// eSuffix, eCSP
|
return false;
|
||||||
//
|
}
|
||||||
//
|
|
||||||
// Given an inner content principal:
|
return true;
|
||||||
// {"0": "https://mozilla.com", "2": "^privateBrowsingId=1"}
|
}
|
||||||
// | | | |
|
|
||||||
// ----------------------------- |
|
template <typename HandlerTypesT>
|
||||||
// | | |
|
bool ContainerPrincipalJSONHandler<HandlerTypesT>::propertyName(
|
||||||
// Key ----------------------
|
const JS::Latin1Char* name, size_t length) {
|
||||||
// |
|
if (mInnerHandler.isSome()) {
|
||||||
// Value
|
return CallOnInner(
|
||||||
//
|
[&](auto& aInner) { return aInner.propertyName(name, length); });
|
||||||
// They Key "0" corresponds to ContentPrincipal::eURI
|
}
|
||||||
// They Key "1" corresponds to ContentPrincipal::eSuffix
|
|
||||||
template <typename T>
|
switch (mState) {
|
||||||
static nsTArray<typename T::KeyVal> GetJSONKeys(const Json::Value* aInput) {
|
case State::StartObject: {
|
||||||
int size = T::eMax + 1;
|
if (length != 1) {
|
||||||
nsTArray<typename T::KeyVal> fields;
|
NS_WARNING(
|
||||||
for (int i = 0; i != size; i++) {
|
nsPrintfCString("Unexpected property name length: %zu", length)
|
||||||
typename T::KeyVal* field = fields.AppendElement();
|
.get());
|
||||||
// field->valueWasSerialized returns if the field was found in the
|
mState = State::Error;
|
||||||
// deserialized code. This simplifies the consumers from having to check
|
return false;
|
||||||
// length.
|
|
||||||
field->valueWasSerialized = false;
|
|
||||||
field->key = static_cast<typename T::SerializableKeys>(i);
|
|
||||||
const std::string key = std::to_string(field->key);
|
|
||||||
if (aInput->isMember(key)) {
|
|
||||||
const Json::Value& val = (*aInput)[key];
|
|
||||||
if (val.isString()) {
|
|
||||||
field->value.Append(nsDependentCString(val.asCString()));
|
|
||||||
field->valueWasSerialized = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char key = char(name[0]);
|
||||||
|
switch (key) {
|
||||||
|
case BasePrincipal::NullPrincipalKey:
|
||||||
|
mState = State::NullPrincipal_Inner;
|
||||||
|
mInnerHandler.emplace(VariantType<NullPrincipalJSONHandler>());
|
||||||
|
break;
|
||||||
|
case BasePrincipal::ContentPrincipalKey:
|
||||||
|
mState = State::ContentPrincipal_Inner;
|
||||||
|
mInnerHandler.emplace(VariantType<ContentPrincipalJSONHandler>());
|
||||||
|
break;
|
||||||
|
case BasePrincipal::SystemPrincipalKey:
|
||||||
|
mState = State::SystemPrincipal_Key;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if constexpr (CanContainExpandedPrincipal) {
|
||||||
|
if (key == BasePrincipal::ExpandedPrincipalKey) {
|
||||||
|
mState = State::ExpandedPrincipal_Inner;
|
||||||
|
mInnerHandler.emplace(
|
||||||
|
VariantType<ExpandedPrincipalJSONHandler>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NS_WARNING(
|
||||||
|
nsPrintfCString("Unexpected property name: '%c'", key).get());
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
NS_WARNING("Unexpected property name");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename HandlerTypesT>
|
||||||
|
bool ContainerPrincipalJSONHandler<HandlerTypesT>::endObject() {
|
||||||
|
if (mInnerHandler.isSome()) {
|
||||||
|
return CallOnInner([&](auto& aInner) {
|
||||||
|
if (!aInner.endObject()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (aInner.HasAccepted()) {
|
||||||
|
this->mPrincipal = aInner.mPrincipal.forget();
|
||||||
|
MOZ_ASSERT(this->mPrincipal);
|
||||||
|
mInnerHandler.reset();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mState) {
|
||||||
|
case State::SystemPrincipal_StartObject:
|
||||||
|
mState = State::SystemPrincipal_EndObject;
|
||||||
|
break;
|
||||||
|
case State::SystemPrincipal_EndObject:
|
||||||
|
this->mPrincipal =
|
||||||
|
BasePrincipal::Cast(nsContentUtils::GetSystemPrincipal());
|
||||||
|
mState = State::EndObject;
|
||||||
|
break;
|
||||||
|
case State::NullPrincipal_Inner:
|
||||||
|
mState = State::EndObject;
|
||||||
|
break;
|
||||||
|
case State::ContentPrincipal_Inner:
|
||||||
|
mState = State::EndObject;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if constexpr (CanContainExpandedPrincipal) {
|
||||||
|
if (mState == State::ExpandedPrincipal_Inner) {
|
||||||
|
mState = State::EndObject;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NS_WARNING("Unexpected end of object");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename HandlerTypesT>
|
||||||
|
bool ContainerPrincipalJSONHandler<HandlerTypesT>::startArray() {
|
||||||
|
if constexpr (CanContainExpandedPrincipal) {
|
||||||
|
if (mInnerHandler.isSome()) {
|
||||||
|
return CallOnInner([&](auto& aInner) { return aInner.startArray(); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fields;
|
|
||||||
|
NS_WARNING("Unexpected array value");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename HandlerTypesT>
|
||||||
|
bool ContainerPrincipalJSONHandler<HandlerTypesT>::endArray() {
|
||||||
|
if constexpr (CanContainExpandedPrincipal) {
|
||||||
|
if (mInnerHandler.isSome()) {
|
||||||
|
return CallOnInner([&](auto& aInner) { return aInner.endArray(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_WARNING("Unexpected array value");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename HandlerTypesT>
|
||||||
|
bool ContainerPrincipalJSONHandler<HandlerTypesT>::stringValue(
|
||||||
|
const JS::Latin1Char* str, size_t length) {
|
||||||
|
if (mInnerHandler.isSome()) {
|
||||||
|
return CallOnInner(
|
||||||
|
[&](auto& aInner) { return aInner.stringValue(str, length); });
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_WARNING("Unexpected string value");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template class ContainerPrincipalJSONHandler<PrincipalJSONHandlerTypes>;
|
||||||
|
template class ContainerPrincipalJSONHandler<SubsumedPrincipalJSONHandlerTypes>;
|
||||||
|
|
||||||
// Takes a JSON string and parses it turning it into a principal of the
|
// Takes a JSON string and parses it turning it into a principal of the
|
||||||
// corresponding type
|
// corresponding type
|
||||||
//
|
//
|
||||||
@@ -261,110 +337,21 @@ static nsTArray<typename T::KeyVal> GetJSONKeys(const Json::Value* aInput) {
|
|||||||
// SerializableKeys |
|
// SerializableKeys |
|
||||||
// Value
|
// Value
|
||||||
//
|
//
|
||||||
// The string is first deserialized with jsoncpp to get the Json::Value of the
|
|
||||||
// object. The inner JSON object is parsed with GetPrincipalObject which returns
|
|
||||||
// a KeyVal array of the inner object's fields. PrincipalKind is returned by
|
|
||||||
// GetPrincipalObject which is then used to decide which principal
|
|
||||||
// implementation of FromProperties to call. The corresponding FromProperties
|
|
||||||
// call takes the KeyVal fields and turns it into a principal.
|
|
||||||
already_AddRefed<BasePrincipal> BasePrincipal::FromJSON(
|
already_AddRefed<BasePrincipal> BasePrincipal::FromJSON(
|
||||||
const nsACString& aJSON) {
|
const nsACString& aJSON) {
|
||||||
Json::Value root;
|
PrincipalJSONHandler handler;
|
||||||
Json::CharReaderBuilder builder;
|
|
||||||
std::unique_ptr<Json::CharReader> const reader(builder.newCharReader());
|
if (!JS::ParseJSONWithHandler(
|
||||||
bool parseSuccess =
|
reinterpret_cast<const JS::Latin1Char*>(aJSON.BeginReading()),
|
||||||
reader->parse(aJSON.BeginReading(), aJSON.EndReading(), &root, nullptr);
|
aJSON.Length(), &handler)) {
|
||||||
if (!parseSuccess) {
|
NS_WARNING(
|
||||||
|
nsPrintfCString("Unable to parse: %s", aJSON.BeginReading()).get());
|
||||||
MOZ_ASSERT(false,
|
MOZ_ASSERT(false,
|
||||||
"Unable to parse string as JSON to deserialize as a principal");
|
"Unable to parse string as JSON to deserialize as a principal");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FromJSON(root);
|
return handler.Get();
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if an ExpandedPrincipal is using the legacy format, where
|
|
||||||
// sub-principals are Base64 encoded.
|
|
||||||
//
|
|
||||||
// Given a legacy expanded principal:
|
|
||||||
//
|
|
||||||
// *
|
|
||||||
// {"2": {"0": "eyIxIjp7IjAiOiJodHRwczovL2EuY29tLyJ9fQ=="}}
|
|
||||||
// | | |
|
|
||||||
// | ---------- Value
|
|
||||||
// | |
|
|
||||||
// PrincipalKind |
|
|
||||||
// |
|
|
||||||
// SerializableKeys
|
|
||||||
//
|
|
||||||
// The value is a CSV list of Base64 encoded prinipcals. The new format for this
|
|
||||||
// principal is:
|
|
||||||
//
|
|
||||||
// Subsumed principals
|
|
||||||
// |
|
|
||||||
// ------------------------------------
|
|
||||||
// * | |
|
|
||||||
// {"2": {"0": [{"1": {"0": https://mozilla.com"}}]}}
|
|
||||||
// | | |
|
|
||||||
// -------------- Value
|
|
||||||
// |
|
|
||||||
// PrincipalKind
|
|
||||||
//
|
|
||||||
// It is possible to tell these apart by checking the type of the property noted
|
|
||||||
// in both diagrams with an asterisk. In the legacy format the type will be a
|
|
||||||
// string and in the new format it will be an array.
|
|
||||||
static bool IsLegacyFormat(const Json::Value& aValue) {
|
|
||||||
const auto& specs = std::to_string(ExpandedPrincipal::eSpecs);
|
|
||||||
return aValue.isMember(specs) && aValue[specs].isString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */
|
|
||||||
already_AddRefed<BasePrincipal> BasePrincipal::FromJSON(
|
|
||||||
const Json::Value& aJSON) {
|
|
||||||
int principalKind = -1;
|
|
||||||
const Json::Value* value = GetPrincipalObject(aJSON, principalKind);
|
|
||||||
if (!value) {
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, "Unexpected JSON principal %s\n",
|
|
||||||
aJSON.toStyledString().c_str());
|
|
||||||
#endif
|
|
||||||
MOZ_ASSERT(false, "Unexpected JSON to deserialize as a principal");
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
MOZ_ASSERT(principalKind != -1,
|
|
||||||
"PrincipalKind should always be >=0 by this point");
|
|
||||||
|
|
||||||
if (principalKind == eSystemPrincipal) {
|
|
||||||
RefPtr<BasePrincipal> principal =
|
|
||||||
BasePrincipal::Cast(nsContentUtils::GetSystemPrincipal());
|
|
||||||
return principal.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (principalKind == eNullPrincipal) {
|
|
||||||
nsTArray<NullPrincipal::KeyVal> res = GetJSONKeys<NullPrincipal>(value);
|
|
||||||
return NullPrincipal::FromProperties(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (principalKind == eContentPrincipal) {
|
|
||||||
nsTArray<ContentPrincipal::KeyVal> res =
|
|
||||||
GetJSONKeys<ContentPrincipal>(value);
|
|
||||||
return ContentPrincipal::FromProperties(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (principalKind == eExpandedPrincipal) {
|
|
||||||
// Check if expanded principals is stored in the new or the old format. See
|
|
||||||
// comment for `IsLegacyFormat`.
|
|
||||||
if (IsLegacyFormat(*value)) {
|
|
||||||
nsTArray<ExpandedPrincipal::KeyVal> res =
|
|
||||||
GetJSONKeys<ExpandedPrincipal>(value);
|
|
||||||
return ExpandedPrincipal::FromProperties(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ExpandedPrincipal::FromProperties(*value);
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_RELEASE_ASSERT(false, "Unexpected enum to deserialize as a principal");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a JSON representation of the principal.
|
// Returns a JSON representation of the principal.
|
||||||
|
|||||||
@@ -27,9 +27,6 @@ class nsIChannel;
|
|||||||
class nsIReferrerInfo;
|
class nsIReferrerInfo;
|
||||||
class nsISupports;
|
class nsISupports;
|
||||||
class nsIURI;
|
class nsIURI;
|
||||||
namespace Json {
|
|
||||||
class Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
@@ -93,6 +90,15 @@ class BasePrincipal : public nsJSPrincipals {
|
|||||||
eKindMax = eSystemPrincipal
|
eKindMax = eSystemPrincipal
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr char NullPrincipalKey = '0';
|
||||||
|
static_assert(eNullPrincipal == 0);
|
||||||
|
static constexpr char ContentPrincipalKey = '1';
|
||||||
|
static_assert(eContentPrincipal == 1);
|
||||||
|
static constexpr char ExpandedPrincipalKey = '2';
|
||||||
|
static_assert(eExpandedPrincipal == 2);
|
||||||
|
static constexpr char SystemPrincipalKey = '3';
|
||||||
|
static_assert(eSystemPrincipal == 3);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool Is() const {
|
bool Is() const {
|
||||||
return mKind == T::Kind();
|
return mKind == T::Kind();
|
||||||
@@ -195,7 +201,6 @@ class BasePrincipal : public nsJSPrincipals {
|
|||||||
nsresult WriteJSONProperties(JSONWriter& aWriter);
|
nsresult WriteJSONProperties(JSONWriter& aWriter);
|
||||||
|
|
||||||
static already_AddRefed<BasePrincipal> FromJSON(const nsACString& aJSON);
|
static already_AddRefed<BasePrincipal> FromJSON(const nsACString& aJSON);
|
||||||
static already_AddRefed<BasePrincipal> FromJSON(const Json::Value& aJSON);
|
|
||||||
|
|
||||||
// Method to write serializable fields which represent all of the fields to
|
// Method to write serializable fields which represent all of the fields to
|
||||||
// deserialize the principal.
|
// deserialize the principal.
|
||||||
|
|||||||
@@ -39,6 +39,9 @@
|
|||||||
|
|
||||||
#include "nsSerializationHelper.h"
|
#include "nsSerializationHelper.h"
|
||||||
|
|
||||||
|
#include "js/JSON.h"
|
||||||
|
#include "ContentPrincipalJSONHandler.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
|
||||||
NS_IMPL_CLASSINFO(ContentPrincipal, nullptr, 0, NS_PRINCIPAL_CID)
|
NS_IMPL_CLASSINFO(ContentPrincipal, nullptr, 0, NS_PRINCIPAL_CID)
|
||||||
@@ -643,66 +646,147 @@ nsresult ContentPrincipal::WriteJSONInnerProperties(JSONWriter& aWriter) {
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<BasePrincipal> ContentPrincipal::FromProperties(
|
bool ContentPrincipalJSONHandler::startObject() {
|
||||||
nsTArray<ContentPrincipal::KeyVal>& aFields) {
|
switch (mState) {
|
||||||
MOZ_ASSERT(aFields.Length() == eMax + 1, "Must have all the keys");
|
case State::Init:
|
||||||
nsresult rv;
|
mState = State::StartObject;
|
||||||
nsCOMPtr<nsIURI> principalURI;
|
break;
|
||||||
nsCOMPtr<nsIURI> domain;
|
default:
|
||||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
NS_WARNING("Unexpected object value");
|
||||||
OriginAttributes attrs;
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
// The odd structure here is to make the code to not compile
|
|
||||||
// if all the switch enum cases haven't been codified
|
|
||||||
for (const auto& field : aFields) {
|
|
||||||
switch (field.key) {
|
|
||||||
case ContentPrincipal::eURI:
|
|
||||||
if (!field.valueWasSerialized) {
|
|
||||||
MOZ_ASSERT(
|
|
||||||
false,
|
|
||||||
"Content principals require a principal URI in serialized JSON");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
rv = NS_NewURI(getter_AddRefs(principalURI), field.value.get());
|
|
||||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
|
||||||
|
|
||||||
{
|
|
||||||
// Enforce re-parsing about: URIs so that if they change, we
|
|
||||||
// continue to use their new principals correctly.
|
|
||||||
if (principalURI->SchemeIs("about")) {
|
|
||||||
nsAutoCString spec;
|
|
||||||
principalURI->GetSpec(spec);
|
|
||||||
if (NS_FAILED(NS_NewURI(getter_AddRefs(principalURI), spec))) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ContentPrincipal::eDomain:
|
|
||||||
if (field.valueWasSerialized) {
|
|
||||||
rv = NS_NewURI(getter_AddRefs(domain), field.value.get());
|
|
||||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ContentPrincipal::eSuffix:
|
|
||||||
if (field.valueWasSerialized) {
|
|
||||||
bool ok = attrs.PopulateFromSuffix(field.value);
|
|
||||||
if (!ok) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nsAutoCString originNoSuffix;
|
|
||||||
rv = ContentPrincipal::GenerateOriginNoSuffixFromURI(principalURI,
|
|
||||||
originNoSuffix);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<ContentPrincipal> principal =
|
return true;
|
||||||
new ContentPrincipal(principalURI, attrs, originNoSuffix, domain);
|
}
|
||||||
|
|
||||||
return principal.forget();
|
bool ContentPrincipalJSONHandler::propertyName(const JS::Latin1Char* name,
|
||||||
|
size_t length) {
|
||||||
|
switch (mState) {
|
||||||
|
case State::StartObject:
|
||||||
|
case State::AfterPropertyValue: {
|
||||||
|
if (length != 1) {
|
||||||
|
NS_WARNING(
|
||||||
|
nsPrintfCString("Unexpected property name length: %zu", length)
|
||||||
|
.get());
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char key = char(name[0]);
|
||||||
|
switch (key) {
|
||||||
|
case ContentPrincipal::URIKey:
|
||||||
|
mState = State::URIKey;
|
||||||
|
break;
|
||||||
|
case ContentPrincipal::DomainKey:
|
||||||
|
mState = State::DomainKey;
|
||||||
|
break;
|
||||||
|
case ContentPrincipal::SuffixKey:
|
||||||
|
mState = State::SuffixKey;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
NS_WARNING(
|
||||||
|
nsPrintfCString("Unexpected property name: '%c'", key).get());
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
NS_WARNING("Unexpected property name");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContentPrincipalJSONHandler::endObject() {
|
||||||
|
switch (mState) {
|
||||||
|
case State::AfterPropertyValue: {
|
||||||
|
MOZ_ASSERT(mPrincipalURI);
|
||||||
|
// NOTE: mDomain is optional.
|
||||||
|
|
||||||
|
nsAutoCString originNoSuffix;
|
||||||
|
nsresult rv = ContentPrincipal::GenerateOriginNoSuffixFromURI(
|
||||||
|
mPrincipalURI, originNoSuffix);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mPrincipal =
|
||||||
|
new ContentPrincipal(mPrincipalURI, mAttrs, originNoSuffix, mDomain);
|
||||||
|
MOZ_ASSERT(mPrincipal);
|
||||||
|
|
||||||
|
mState = State::EndObject;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
NS_WARNING("Unexpected end of object");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContentPrincipalJSONHandler::stringValue(const JS::Latin1Char* str,
|
||||||
|
size_t length) {
|
||||||
|
switch (mState) {
|
||||||
|
case State::URIKey: {
|
||||||
|
nsDependentCSubstring spec(reinterpret_cast<const char*>(str), length);
|
||||||
|
|
||||||
|
nsresult rv = NS_NewURI(getter_AddRefs(mPrincipalURI), spec);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Enforce re-parsing about: URIs so that if they change, we
|
||||||
|
// continue to use their new principals correctly.
|
||||||
|
if (mPrincipalURI->SchemeIs("about")) {
|
||||||
|
nsAutoCString spec;
|
||||||
|
mPrincipalURI->GetSpec(spec);
|
||||||
|
rv = NS_NewURI(getter_AddRefs(mPrincipalURI), spec);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mState = State::AfterPropertyValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case State::DomainKey: {
|
||||||
|
nsDependentCSubstring spec(reinterpret_cast<const char*>(str), length);
|
||||||
|
|
||||||
|
nsresult rv = NS_NewURI(getter_AddRefs(mDomain), spec);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mState = State::AfterPropertyValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case State::SuffixKey: {
|
||||||
|
nsDependentCSubstring attrs(reinterpret_cast<const char*>(str), length);
|
||||||
|
if (!mAttrs.PopulateFromSuffix(attrs)) {
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mState = State::AfterPropertyValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
NS_WARNING("Unexpected string value");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,10 +56,13 @@ class ContentPrincipal final : public BasePrincipal {
|
|||||||
eSuffix,
|
eSuffix,
|
||||||
eMax = eSuffix
|
eMax = eSuffix
|
||||||
};
|
};
|
||||||
typedef mozilla::BasePrincipal::KeyValT<SerializableKeys> KeyVal;
|
|
||||||
|
|
||||||
static already_AddRefed<BasePrincipal> FromProperties(
|
static constexpr char URIKey = '0';
|
||||||
nsTArray<ContentPrincipal::KeyVal>& aFields);
|
static_assert(eURI == 0);
|
||||||
|
static constexpr char DomainKey = '1';
|
||||||
|
static_assert(eDomain == 1);
|
||||||
|
static constexpr char SuffixKey = '2';
|
||||||
|
static_assert(eSuffix == 2);
|
||||||
|
|
||||||
class Deserializer : public BasePrincipal::Deserializer {
|
class Deserializer : public BasePrincipal::Deserializer {
|
||||||
public:
|
public:
|
||||||
|
|||||||
94
caps/ContentPrincipalJSONHandler.h
Normal file
94
caps/ContentPrincipalJSONHandler.h
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_ContentPrincipalJSONHandler_h
|
||||||
|
#define mozilla_ContentPrincipalJSONHandler_h
|
||||||
|
|
||||||
|
#include <stddef.h> // size_t
|
||||||
|
#include <stdint.h> // uint32_t
|
||||||
|
|
||||||
|
#include "js/TypeDecls.h" // JS::Latin1Char
|
||||||
|
|
||||||
|
#include "mozilla/AlreadyAddRefed.h" // already_AddRefed
|
||||||
|
#include "mozilla/RefPtr.h" // RefPtr
|
||||||
|
|
||||||
|
#include "nsCOMPtr.h" // nsCOMPtr
|
||||||
|
#include "nsDebug.h" // NS_WARNING
|
||||||
|
#include "nsIURI.h" // nsIURI
|
||||||
|
|
||||||
|
#include "ContentPrincipal.h"
|
||||||
|
#include "OriginAttributes.h"
|
||||||
|
#include "SharedJSONHandler.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
// JSON parse handler for an inner object for ContentPrincipal.
|
||||||
|
// Used by PrincipalJSONHandler or SubsumedPrincipalJSONHandler.
|
||||||
|
class ContentPrincipalJSONHandler : public PrincipalJSONHandlerShared {
|
||||||
|
enum class State {
|
||||||
|
Init,
|
||||||
|
|
||||||
|
// After the inner object's '{'.
|
||||||
|
StartObject,
|
||||||
|
|
||||||
|
// After the property key for eURI.
|
||||||
|
URIKey,
|
||||||
|
|
||||||
|
// After the property key for eDomain.
|
||||||
|
DomainKey,
|
||||||
|
|
||||||
|
// After the property key for eSuffix.
|
||||||
|
SuffixKey,
|
||||||
|
|
||||||
|
// After the property value for eURI, eDomain, or eSuffix.
|
||||||
|
AfterPropertyValue,
|
||||||
|
|
||||||
|
// After the inner object's '}'.
|
||||||
|
EndObject,
|
||||||
|
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
ContentPrincipalJSONHandler() = default;
|
||||||
|
virtual ~ContentPrincipalJSONHandler() = default;
|
||||||
|
|
||||||
|
virtual bool startObject() override;
|
||||||
|
|
||||||
|
using PrincipalJSONHandlerShared::propertyName;
|
||||||
|
virtual bool propertyName(const JS::Latin1Char* name, size_t length) override;
|
||||||
|
|
||||||
|
virtual bool endObject() override;
|
||||||
|
|
||||||
|
virtual bool startArray() override {
|
||||||
|
NS_WARNING("Unexpected array value");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
virtual bool endArray() override {
|
||||||
|
NS_WARNING("Unexpected array value");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
using PrincipalJSONHandlerShared::stringValue;
|
||||||
|
virtual bool stringValue(const JS::Latin1Char* str, size_t length) override;
|
||||||
|
|
||||||
|
bool HasAccepted() const { return mState == State::EndObject; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void SetErrorState() override { mState = State::Error; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
State mState = State::Init;
|
||||||
|
|
||||||
|
nsCOMPtr<nsIURI> mPrincipalURI;
|
||||||
|
nsCOMPtr<nsIURI> mDomain;
|
||||||
|
OriginAttributes mAttrs;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_ContentPrincipalJSONHandler_h
|
||||||
@@ -11,7 +11,10 @@
|
|||||||
#include "mozilla/Base64.h"
|
#include "mozilla/Base64.h"
|
||||||
#include "mozilla/extensions/WebExtensionPolicy.h"
|
#include "mozilla/extensions/WebExtensionPolicy.h"
|
||||||
#include "mozilla/JSONWriter.h"
|
#include "mozilla/JSONWriter.h"
|
||||||
#include "json/json.h"
|
|
||||||
|
#include "js/JSON.h"
|
||||||
|
#include "ExpandedPrincipalJSONHandler.h"
|
||||||
|
#include "SubsumedPrincipalJSONHandler.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
|
||||||
@@ -287,100 +290,187 @@ nsresult ExpandedPrincipal::WriteJSONInnerProperties(JSONWriter& aWriter) {
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<BasePrincipal> ExpandedPrincipal::FromProperties(
|
bool ExpandedPrincipalJSONHandler::ProcessSubsumedResult(bool aResult) {
|
||||||
nsTArray<ExpandedPrincipal::KeyVal>& aFields) {
|
if (!aResult) {
|
||||||
MOZ_ASSERT(aFields.Length() == eMax + 1, "Must have all the keys");
|
NS_WARNING("Failed to parse subsumed principal");
|
||||||
nsTArray<nsCOMPtr<nsIPrincipal>> allowList;
|
mState = State::Error;
|
||||||
OriginAttributes attrs;
|
return false;
|
||||||
// The odd structure here is to make the code to not compile
|
|
||||||
// if all the switch enum cases haven't been codified
|
|
||||||
|
|
||||||
for (const auto& field : aFields) {
|
|
||||||
switch (field.key) {
|
|
||||||
case ExpandedPrincipal::eSpecs:
|
|
||||||
if (!field.valueWasSerialized) {
|
|
||||||
MOZ_ASSERT(false,
|
|
||||||
"Expanded principals require specs in serialized JSON");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
for (const nsACString& each : field.value.Split(',')) {
|
|
||||||
nsAutoCString result;
|
|
||||||
nsresult rv;
|
|
||||||
rv = Base64Decode(each, result);
|
|
||||||
MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to decode");
|
|
||||||
|
|
||||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
|
||||||
nsCOMPtr<nsIPrincipal> principal = BasePrincipal::FromJSON(result);
|
|
||||||
allowList.AppendElement(principal);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ExpandedPrincipal::eSuffix:
|
|
||||||
if (field.valueWasSerialized) {
|
|
||||||
bool ok = attrs.PopulateFromSuffix(field.value);
|
|
||||||
if (!ok) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
if (allowList.Length() == 0) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<ExpandedPrincipal> expandedPrincipal =
|
|
||||||
ExpandedPrincipal::Create(allowList, attrs);
|
|
||||||
|
|
||||||
return expandedPrincipal.forget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
bool ExpandedPrincipalJSONHandler::startObject() {
|
||||||
already_AddRefed<BasePrincipal> ExpandedPrincipal::FromProperties(
|
if (mSubsumedHandler.isSome()) {
|
||||||
const Json::Value& aJSON) {
|
return ProcessSubsumedResult(mSubsumedHandler->startObject());
|
||||||
MOZ_ASSERT(aJSON.size() <= eMax + 1, "Must have at most, all the properties");
|
|
||||||
const std::string specs = std::to_string(eSpecs);
|
|
||||||
const std::string suffix = std::to_string(eSuffix);
|
|
||||||
MOZ_ASSERT(aJSON.isMember(specs), "The eSpecs member is required");
|
|
||||||
MOZ_ASSERT(aJSON.size() == 1 || aJSON.isMember(suffix),
|
|
||||||
"eSuffix is optional");
|
|
||||||
|
|
||||||
const auto* specsValue =
|
|
||||||
aJSON.find(specs.c_str(), specs.c_str() + specs.length());
|
|
||||||
if (!specsValue) {
|
|
||||||
MOZ_ASSERT(false, "Expanded principals require specs in serialized JSON");
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsTArray<nsCOMPtr<nsIPrincipal>> allowList;
|
switch (mState) {
|
||||||
for (const auto& principalJSON : *specsValue) {
|
case State::Init:
|
||||||
if (nsCOMPtr<nsIPrincipal> principal =
|
mState = State::StartObject;
|
||||||
BasePrincipal::FromJSON(principalJSON)) {
|
break;
|
||||||
allowList.AppendElement(principal);
|
case State::StartArray:
|
||||||
|
mState = State::SubsumedPrincipal;
|
||||||
|
[[fallthrough]];
|
||||||
|
case State::SubsumedPrincipal:
|
||||||
|
mSubsumedHandler.emplace();
|
||||||
|
|
||||||
|
return ProcessSubsumedResult(mSubsumedHandler->startObject());
|
||||||
|
default:
|
||||||
|
NS_WARNING("Unexpected object value");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExpandedPrincipalJSONHandler::propertyName(const JS::Latin1Char* name,
|
||||||
|
size_t length) {
|
||||||
|
if (mSubsumedHandler.isSome()) {
|
||||||
|
return ProcessSubsumedResult(mSubsumedHandler->propertyName(name, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mState) {
|
||||||
|
case State::StartObject:
|
||||||
|
case State::AfterPropertyValue: {
|
||||||
|
if (length != 1) {
|
||||||
|
NS_WARNING(
|
||||||
|
nsPrintfCString("Unexpected property name length: %zu", length)
|
||||||
|
.get());
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char key = char(name[0]);
|
||||||
|
switch (key) {
|
||||||
|
case ExpandedPrincipal::SpecsKey:
|
||||||
|
mState = State::SpecsKey;
|
||||||
|
break;
|
||||||
|
case ExpandedPrincipal::SuffixKey:
|
||||||
|
mState = State::SuffixKey;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
NS_WARNING(
|
||||||
|
nsPrintfCString("Unexpected property name: '%c'", key).get());
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
NS_WARNING("Unexpected property name");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowList.Length() == 0) {
|
return true;
|
||||||
return nullptr;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
OriginAttributes attrs;
|
bool ExpandedPrincipalJSONHandler::endObject() {
|
||||||
if (aJSON.isMember(suffix)) {
|
if (mSubsumedHandler.isSome()) {
|
||||||
const auto& value = aJSON[suffix];
|
if (!ProcessSubsumedResult(mSubsumedHandler->endObject())) {
|
||||||
if (!value.isString()) {
|
return false;
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
if (mSubsumedHandler->HasAccepted()) {
|
||||||
bool ok = attrs.PopulateFromSuffix(nsDependentCString(value.asCString()));
|
nsCOMPtr<nsIPrincipal> principal = mSubsumedHandler->mPrincipal.forget();
|
||||||
if (!ok) {
|
mSubsumedHandler.reset();
|
||||||
return nullptr;
|
mAllowList.AppendElement(principal);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<ExpandedPrincipal> expandedPrincipal =
|
switch (mState) {
|
||||||
ExpandedPrincipal::Create(allowList, attrs);
|
case State::AfterPropertyValue:
|
||||||
|
mPrincipal = ExpandedPrincipal::Create(mAllowList, mAttrs);
|
||||||
|
MOZ_ASSERT(mPrincipal);
|
||||||
|
|
||||||
return expandedPrincipal.forget();
|
mState = State::EndObject;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
NS_WARNING("Unexpected end of object");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExpandedPrincipalJSONHandler::startArray() {
|
||||||
|
switch (mState) {
|
||||||
|
case State::SpecsKey:
|
||||||
|
mState = State::StartArray;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
NS_WARNING("Unexpected array value");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExpandedPrincipalJSONHandler::endArray() {
|
||||||
|
switch (mState) {
|
||||||
|
case State::SubsumedPrincipal: {
|
||||||
|
mState = State::AfterPropertyValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
NS_WARNING("Unexpected end of array");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExpandedPrincipalJSONHandler::stringValue(const JS::Latin1Char* str,
|
||||||
|
size_t length) {
|
||||||
|
if (mSubsumedHandler.isSome()) {
|
||||||
|
return ProcessSubsumedResult(mSubsumedHandler->stringValue(str, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mState) {
|
||||||
|
case State::SpecsKey: {
|
||||||
|
nsDependentCSubstring specs(reinterpret_cast<const char*>(str), length);
|
||||||
|
|
||||||
|
for (const nsACString& each : specs.Split(',')) {
|
||||||
|
nsAutoCString result;
|
||||||
|
nsresult rv = Base64Decode(each, result);
|
||||||
|
MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to decode");
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIPrincipal> principal = BasePrincipal::FromJSON(result);
|
||||||
|
if (!principal) {
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mAllowList.AppendElement(principal);
|
||||||
|
}
|
||||||
|
|
||||||
|
mState = State::AfterPropertyValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case State::SuffixKey: {
|
||||||
|
nsDependentCSubstring attrs(reinterpret_cast<const char*>(str), length);
|
||||||
|
if (!mAttrs.PopulateFromSuffix(attrs)) {
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mState = State::AfterPropertyValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
NS_WARNING("Unexpected string value");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
|
|||||||
@@ -15,10 +15,6 @@
|
|||||||
|
|
||||||
class nsIContentSecurityPolicy;
|
class nsIContentSecurityPolicy;
|
||||||
|
|
||||||
namespace Json {
|
|
||||||
class Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
class JSONWriter;
|
class JSONWriter;
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
@@ -66,17 +62,11 @@ class ExpandedPrincipal : public nsIExpandedPrincipal,
|
|||||||
|
|
||||||
// Serializable keys are the valid enum fields the serialization supports
|
// Serializable keys are the valid enum fields the serialization supports
|
||||||
enum SerializableKeys : uint8_t { eSpecs = 0, eSuffix, eMax = eSuffix };
|
enum SerializableKeys : uint8_t { eSpecs = 0, eSuffix, eMax = eSuffix };
|
||||||
typedef mozilla::BasePrincipal::KeyValT<SerializableKeys> KeyVal;
|
|
||||||
|
|
||||||
// This is the legacy serializer for expanded principals. See note for
|
static constexpr char SpecsKey = '0';
|
||||||
// `IsLegacyFormat` in BasePrincipal.cpp.
|
static_assert(eSpecs == 0);
|
||||||
static already_AddRefed<BasePrincipal> FromProperties(
|
static constexpr char SuffixKey = '1';
|
||||||
nsTArray<ExpandedPrincipal::KeyVal>& aFields);
|
static_assert(eSuffix == 1);
|
||||||
|
|
||||||
// This is the new serializer for expanded principals. See note for
|
|
||||||
// `IsLegacyFormat` in BasePrincipal.cpp.
|
|
||||||
static already_AddRefed<BasePrincipal> FromProperties(
|
|
||||||
const Json::Value& aJSON);
|
|
||||||
|
|
||||||
class Deserializer : public BasePrincipal::Deserializer {
|
class Deserializer : public BasePrincipal::Deserializer {
|
||||||
public:
|
public:
|
||||||
|
|||||||
130
caps/ExpandedPrincipalJSONHandler.h
Normal file
130
caps/ExpandedPrincipalJSONHandler.h
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_ExpandedPrincipalJSONHandler_h
|
||||||
|
#define mozilla_ExpandedPrincipalJSONHandler_h
|
||||||
|
|
||||||
|
#include <stddef.h> // size_t
|
||||||
|
#include <stdint.h> // uint32_t
|
||||||
|
|
||||||
|
#include "js/TypeDecls.h" // JS::Latin1Char
|
||||||
|
|
||||||
|
#include "mozilla/AlreadyAddRefed.h" // already_AddRefed
|
||||||
|
#include "mozilla/Maybe.h" // Maybe
|
||||||
|
#include "mozilla/RefPtr.h" // RefPtr
|
||||||
|
|
||||||
|
#include "nsCOMPtr.h" // nsCOMPtr
|
||||||
|
#include "nsDebug.h" // NS_WARNING
|
||||||
|
#include "nsIPrincipal.h" // nsIPrincipal
|
||||||
|
#include "nsTArray.h" // nsTArray
|
||||||
|
|
||||||
|
#include "OriginAttributes.h"
|
||||||
|
#include "ExpandedPrincipal.h"
|
||||||
|
#include "SubsumedPrincipalJSONHandler.h"
|
||||||
|
#include "SharedJSONHandler.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
// JSON parse handler for an inner object for ExpandedPrincipal.
|
||||||
|
//
|
||||||
|
// # Legacy format
|
||||||
|
//
|
||||||
|
// inner object
|
||||||
|
// |
|
||||||
|
// -------------------------------------------------
|
||||||
|
// | |
|
||||||
|
// {"2": {"0": "eyIxIjp7IjAiOiJodHRwczovL2EuY29tLyJ9fQ=="}}
|
||||||
|
// | | |
|
||||||
|
// | ---------- Value
|
||||||
|
// | |
|
||||||
|
// PrincipalKind |
|
||||||
|
// |
|
||||||
|
// SerializableKeys
|
||||||
|
//
|
||||||
|
// The value is a CSV list of Base64 encoded prinipcals.
|
||||||
|
//
|
||||||
|
// # New format
|
||||||
|
//
|
||||||
|
// inner object
|
||||||
|
// |
|
||||||
|
// -------------------------------------------
|
||||||
|
// | |
|
||||||
|
// | Subsumed principals |
|
||||||
|
// | | |
|
||||||
|
// | ------------------------------------|
|
||||||
|
// | | ||
|
||||||
|
// {"2": {"0": [{"1": {"0": https://mozilla.com"}}]}}
|
||||||
|
// | | |
|
||||||
|
// -------------- Value
|
||||||
|
// |
|
||||||
|
// PrincipalKind
|
||||||
|
//
|
||||||
|
// Used by PrincipalJSONHandler.
|
||||||
|
class ExpandedPrincipalJSONHandler : public PrincipalJSONHandlerShared {
|
||||||
|
enum class State {
|
||||||
|
Init,
|
||||||
|
|
||||||
|
// After the inner object's '{'.
|
||||||
|
StartObject,
|
||||||
|
|
||||||
|
// After the property key for eSpecs.
|
||||||
|
SpecsKey,
|
||||||
|
|
||||||
|
// After the property key for eSuffix.
|
||||||
|
SuffixKey,
|
||||||
|
|
||||||
|
// After the subsumed principals array's '['.
|
||||||
|
StartArray,
|
||||||
|
|
||||||
|
// Subsumed principals array's item.
|
||||||
|
// Delegates to mSubsumedHandler until the subsumed object's '}'.
|
||||||
|
SubsumedPrincipal,
|
||||||
|
|
||||||
|
// After the property value for eSpecs or eSuffix,
|
||||||
|
// including after the subsumed principals array's ']'.
|
||||||
|
AfterPropertyValue,
|
||||||
|
|
||||||
|
// After the inner object's '}'.
|
||||||
|
EndObject,
|
||||||
|
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
ExpandedPrincipalJSONHandler() = default;
|
||||||
|
virtual ~ExpandedPrincipalJSONHandler() = default;
|
||||||
|
|
||||||
|
virtual bool startObject() override;
|
||||||
|
|
||||||
|
using PrincipalJSONHandlerShared::propertyName;
|
||||||
|
virtual bool propertyName(const JS::Latin1Char* name, size_t length) override;
|
||||||
|
|
||||||
|
virtual bool endObject() override;
|
||||||
|
|
||||||
|
virtual bool startArray() override;
|
||||||
|
virtual bool endArray() override;
|
||||||
|
|
||||||
|
using PrincipalJSONHandlerShared::stringValue;
|
||||||
|
virtual bool stringValue(const JS::Latin1Char* str, size_t length) override;
|
||||||
|
|
||||||
|
bool HasAccepted() const { return mState == State::EndObject; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void SetErrorState() override { mState = State::Error; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool ProcessSubsumedResult(bool aResult);
|
||||||
|
|
||||||
|
private:
|
||||||
|
State mState = State::Init;
|
||||||
|
|
||||||
|
nsTArray<nsCOMPtr<nsIPrincipal>> mAllowList;
|
||||||
|
OriginAttributes mAttrs;
|
||||||
|
Maybe<SubsumedPrincipalJSONHandler> mSubsumedHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_ExpandedPrincipalJSONHandler_h
|
||||||
@@ -27,6 +27,9 @@
|
|||||||
#include "pratom.h"
|
#include "pratom.h"
|
||||||
#include "nsIObjectInputStream.h"
|
#include "nsIObjectInputStream.h"
|
||||||
|
|
||||||
|
#include "js/JSON.h"
|
||||||
|
#include "NullPrincipalJSONHandler.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
|
||||||
NS_IMPL_CLASSINFO(NullPrincipal, nullptr, 0, NS_NULLPRINCIPAL_CID)
|
NS_IMPL_CLASSINFO(NullPrincipal, nullptr, 0, NS_NULLPRINCIPAL_CID)
|
||||||
@@ -249,43 +252,6 @@ nsresult NullPrincipal::WriteJSONInnerProperties(JSONWriter& aWriter) {
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<BasePrincipal> NullPrincipal::FromProperties(
|
|
||||||
nsTArray<NullPrincipal::KeyVal>& aFields) {
|
|
||||||
MOZ_ASSERT(aFields.Length() == eMax + 1, "Must have all the keys");
|
|
||||||
nsresult rv;
|
|
||||||
nsCOMPtr<nsIURI> uri;
|
|
||||||
OriginAttributes attrs;
|
|
||||||
|
|
||||||
// The odd structure here is to make the code to not compile
|
|
||||||
// if all the switch enum cases haven't been codified
|
|
||||||
for (const auto& field : aFields) {
|
|
||||||
switch (field.key) {
|
|
||||||
case NullPrincipal::eSpec:
|
|
||||||
if (!field.valueWasSerialized) {
|
|
||||||
MOZ_ASSERT(false,
|
|
||||||
"Null principals require a spec URI in serialized JSON");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
rv = NS_NewURI(getter_AddRefs(uri), field.value);
|
|
||||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
|
||||||
break;
|
|
||||||
case NullPrincipal::eSuffix:
|
|
||||||
bool ok = attrs.PopulateFromSuffix(field.value);
|
|
||||||
if (!ok) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!uri) {
|
|
||||||
MOZ_ASSERT(false, "No URI deserialized");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NullPrincipal::Create(attrs, uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
NullPrincipal::GetPrecursorPrincipal(nsIPrincipal** aPrincipal) {
|
NullPrincipal::GetPrecursorPrincipal(nsIPrincipal** aPrincipal) {
|
||||||
*aPrincipal = nullptr;
|
*aPrincipal = nullptr;
|
||||||
@@ -329,3 +295,107 @@ NullPrincipal::GetPrecursorPrincipal(nsIPrincipal** aPrincipal) {
|
|||||||
contentPrincipal.forget(aPrincipal);
|
contentPrincipal.forget(aPrincipal);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NullPrincipalJSONHandler::startObject() {
|
||||||
|
switch (mState) {
|
||||||
|
case State::Init:
|
||||||
|
mState = State::StartObject;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
NS_WARNING("Unexpected object value");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NullPrincipalJSONHandler::propertyName(const JS::Latin1Char* name,
|
||||||
|
size_t length) {
|
||||||
|
switch (mState) {
|
||||||
|
case State::StartObject:
|
||||||
|
case State::AfterPropertyValue: {
|
||||||
|
if (length != 1) {
|
||||||
|
NS_WARNING(
|
||||||
|
nsPrintfCString("Unexpected property name length: %zu", length)
|
||||||
|
.get());
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char key = char(name[0]);
|
||||||
|
switch (key) {
|
||||||
|
case NullPrincipal::SpecKey:
|
||||||
|
mState = State::SpecKey;
|
||||||
|
break;
|
||||||
|
case NullPrincipal::SuffixKey:
|
||||||
|
mState = State::SuffixKey;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
NS_WARNING(
|
||||||
|
nsPrintfCString("Unexpected property name: '%c'", key).get());
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
NS_WARNING("Unexpected property name");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NullPrincipalJSONHandler::endObject() {
|
||||||
|
switch (mState) {
|
||||||
|
case State::AfterPropertyValue:
|
||||||
|
MOZ_ASSERT(mUri);
|
||||||
|
|
||||||
|
mPrincipal = NullPrincipal::Create(mAttrs, mUri);
|
||||||
|
MOZ_ASSERT(mPrincipal);
|
||||||
|
|
||||||
|
mState = State::EndObject;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
NS_WARNING("Unexpected end of object");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NullPrincipalJSONHandler::stringValue(const JS::Latin1Char* str,
|
||||||
|
size_t length) {
|
||||||
|
switch (mState) {
|
||||||
|
case State::SpecKey: {
|
||||||
|
nsDependentCSubstring spec(reinterpret_cast<const char*>(str), length);
|
||||||
|
nsresult rv = NS_NewURI(getter_AddRefs(mUri), spec);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mState = State::AfterPropertyValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case State::SuffixKey: {
|
||||||
|
nsDependentCSubstring attrs(reinterpret_cast<const char*>(str), length);
|
||||||
|
if (!mAttrs.PopulateFromSuffix(attrs)) {
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mState = State::AfterPropertyValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
NS_WARNING("Unexpected string value");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -88,10 +88,11 @@ class NullPrincipal final : public BasePrincipal {
|
|||||||
|
|
||||||
// Serializable keys are the valid enum fields the serialization supports
|
// Serializable keys are the valid enum fields the serialization supports
|
||||||
enum SerializableKeys : uint8_t { eSpec = 0, eSuffix, eMax = eSuffix };
|
enum SerializableKeys : uint8_t { eSpec = 0, eSuffix, eMax = eSuffix };
|
||||||
typedef mozilla::BasePrincipal::KeyValT<SerializableKeys> KeyVal;
|
|
||||||
|
|
||||||
static already_AddRefed<BasePrincipal> FromProperties(
|
static constexpr char SpecKey = '0';
|
||||||
nsTArray<NullPrincipal::KeyVal>& aFields);
|
static_assert(eSpec == 0);
|
||||||
|
static constexpr char SuffixKey = '1';
|
||||||
|
static_assert(eSuffix == 1);
|
||||||
|
|
||||||
class Deserializer : public BasePrincipal::Deserializer {
|
class Deserializer : public BasePrincipal::Deserializer {
|
||||||
public:
|
public:
|
||||||
|
|||||||
91
caps/NullPrincipalJSONHandler.h
Normal file
91
caps/NullPrincipalJSONHandler.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_NullPrincipalJSONHandler_h
|
||||||
|
#define mozilla_NullPrincipalJSONHandler_h
|
||||||
|
|
||||||
|
#include <stddef.h> // size_t
|
||||||
|
#include <stdint.h> // uint32_t
|
||||||
|
|
||||||
|
#include "js/TypeDecls.h" // JS::Latin1Char
|
||||||
|
|
||||||
|
#include "mozilla/AlreadyAddRefed.h" // already_AddRefed
|
||||||
|
#include "mozilla/Assertions.h" // MOZ_ASSERT_UNREACHABLE
|
||||||
|
#include "mozilla/RefPtr.h" // RefPtr
|
||||||
|
|
||||||
|
#include "nsCOMPtr.h" // nsCOMPtr
|
||||||
|
#include "nsDebug.h" // NS_WARNING
|
||||||
|
#include "nsIURI.h" // nsIURI
|
||||||
|
|
||||||
|
#include "NullPrincipal.h"
|
||||||
|
#include "OriginAttributes.h"
|
||||||
|
#include "SharedJSONHandler.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
// JSON parse handler for an inner object for NullPrincipal.
|
||||||
|
// Used by PrincipalJSONHandler or SubsumedPrincipalJSONHandler.
|
||||||
|
class NullPrincipalJSONHandler : public PrincipalJSONHandlerShared {
|
||||||
|
enum class State {
|
||||||
|
Init,
|
||||||
|
|
||||||
|
// After the inner object's '{'.
|
||||||
|
StartObject,
|
||||||
|
|
||||||
|
// After the property key for eSpec.
|
||||||
|
SpecKey,
|
||||||
|
|
||||||
|
// After the property key for eSuffix.
|
||||||
|
SuffixKey,
|
||||||
|
|
||||||
|
// After the property value for eSpec or eSuffix.
|
||||||
|
AfterPropertyValue,
|
||||||
|
|
||||||
|
// After the inner object's '}'.
|
||||||
|
EndObject,
|
||||||
|
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
NullPrincipalJSONHandler() = default;
|
||||||
|
virtual ~NullPrincipalJSONHandler() = default;
|
||||||
|
|
||||||
|
virtual bool startObject() override;
|
||||||
|
|
||||||
|
using PrincipalJSONHandlerShared::propertyName;
|
||||||
|
virtual bool propertyName(const JS::Latin1Char* name, size_t length) override;
|
||||||
|
|
||||||
|
virtual bool endObject() override;
|
||||||
|
|
||||||
|
virtual bool startArray() override {
|
||||||
|
NS_WARNING("Unexpected array value");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
virtual bool endArray() override {
|
||||||
|
NS_WARNING("Unexpected array value");
|
||||||
|
mState = State::Error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
using PrincipalJSONHandlerShared::stringValue;
|
||||||
|
virtual bool stringValue(const JS::Latin1Char* str, size_t length) override;
|
||||||
|
|
||||||
|
bool HasAccepted() const { return mState == State::EndObject; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void SetErrorState() override { mState = State::Error; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
State mState = State::Init;
|
||||||
|
|
||||||
|
nsCOMPtr<nsIURI> mUri;
|
||||||
|
OriginAttributes mAttrs;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_NullPrincipalJSONHandler_h
|
||||||
116
caps/PrincipalJSONHandler.h
Normal file
116
caps/PrincipalJSONHandler.h
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_PrincipalJSONHandler_h
|
||||||
|
#define mozilla_PrincipalJSONHandler_h
|
||||||
|
|
||||||
|
#include <stddef.h> // size_t
|
||||||
|
#include <stdint.h> // uint32_t
|
||||||
|
|
||||||
|
#include "js/JSON.h" // JS::JSONParseHandler
|
||||||
|
#include "js/TypeDecls.h" // JS::Latin1Char
|
||||||
|
|
||||||
|
#include "mozilla/AlreadyAddRefed.h" // already_AddRefed
|
||||||
|
#include "mozilla/RefPtr.h" // RefPtr
|
||||||
|
#include "mozilla/Variant.h" // Variant
|
||||||
|
|
||||||
|
#include "nsDebug.h" // NS_WARNING
|
||||||
|
#include "nsPrintfCString.h" // nsPrintfCString
|
||||||
|
|
||||||
|
#include "BasePrincipal.h"
|
||||||
|
#include "ContentPrincipalJSONHandler.h"
|
||||||
|
#include "ExpandedPrincipalJSONHandler.h"
|
||||||
|
#include "NullPrincipalJSONHandler.h"
|
||||||
|
#include "SharedJSONHandler.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
class PrincipalJSONHandlerTypes {
|
||||||
|
public:
|
||||||
|
enum class State {
|
||||||
|
Init,
|
||||||
|
|
||||||
|
// After top-level object's '{'.
|
||||||
|
StartObject,
|
||||||
|
|
||||||
|
// After the PrincipalKind property key for SystemPrincipal.
|
||||||
|
SystemPrincipal_Key,
|
||||||
|
// After the SystemPrincipal's inner object's '{'.
|
||||||
|
SystemPrincipal_StartObject,
|
||||||
|
// After the SystemPrincipal's inner object's '}'.
|
||||||
|
SystemPrincipal_EndObject,
|
||||||
|
|
||||||
|
// After the PrincipalKind property key for NullPrincipal, ContentPrincipal,
|
||||||
|
// or ExpandedPrincipal, and also the entire inner object.
|
||||||
|
// Delegates to mInnerHandler until the inner object's '}'.
|
||||||
|
NullPrincipal_Inner,
|
||||||
|
ContentPrincipal_Inner,
|
||||||
|
ExpandedPrincipal_Inner,
|
||||||
|
|
||||||
|
// After top-level object's '}'.
|
||||||
|
EndObject,
|
||||||
|
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
using InnerHandlerT =
|
||||||
|
Maybe<Variant<NullPrincipalJSONHandler, ContentPrincipalJSONHandler,
|
||||||
|
ExpandedPrincipalJSONHandler>>;
|
||||||
|
|
||||||
|
static constexpr bool CanContainExpandedPrincipal = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// JSON parse handler for the top-level object for principal.
|
||||||
|
//
|
||||||
|
// inner object
|
||||||
|
// |
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// | |
|
||||||
|
// {"1": {"0": "https://mozilla.com", "2": "^privateBrowsingId=1"}}
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// PrincipalKind
|
||||||
|
//
|
||||||
|
// For inner object except for the system principal case, this delegates
|
||||||
|
// to NullPrincipalJSONHandler, ContentPrincipalJSONHandler, or
|
||||||
|
// ExpandedPrincipalJSONHandler.
|
||||||
|
//
|
||||||
|
//// Null principal:
|
||||||
|
// {"0":{"0":"moz-nullprincipal:{56cac540-864d-47e7-8e25-1614eab5155e}"}}
|
||||||
|
//
|
||||||
|
// Content principal:
|
||||||
|
// {"1":{"0":"https://mozilla.com"}} -> {"0":"https://mozilla.com"}
|
||||||
|
//
|
||||||
|
// Expanded principal:
|
||||||
|
// {"2":{"0":"<base64principal1>,<base64principal2>"}}
|
||||||
|
//
|
||||||
|
// System principal:
|
||||||
|
// {"3":{}}
|
||||||
|
class PrincipalJSONHandler
|
||||||
|
: public ContainerPrincipalJSONHandler<PrincipalJSONHandlerTypes> {
|
||||||
|
using State = PrincipalJSONHandlerTypes::State;
|
||||||
|
using InnerHandlerT = PrincipalJSONHandlerTypes::InnerHandlerT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PrincipalJSONHandler() = default;
|
||||||
|
virtual ~PrincipalJSONHandler() = default;
|
||||||
|
|
||||||
|
virtual void error(const char* msg, uint32_t line, uint32_t column) override {
|
||||||
|
NS_WARNING(
|
||||||
|
nsPrintfCString("JSON Error: %s at line %u column %u of the JSON data",
|
||||||
|
msg, line, column)
|
||||||
|
.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<BasePrincipal> Get() { return mPrincipal.forget(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void SetErrorState() override { mState = State::Error; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_PrincipalJSONHandler_h
|
||||||
114
caps/SharedJSONHandler.h
Normal file
114
caps/SharedJSONHandler.h
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_SharedJSONHandler_h
|
||||||
|
#define mozilla_SharedJSONHandler_h
|
||||||
|
|
||||||
|
#include "js/JSON.h" // JS::JSONParseHandler
|
||||||
|
|
||||||
|
#include "mozilla/RefPtr.h" // RefPtr
|
||||||
|
|
||||||
|
#include "BasePrincipal.h" // BasePrincipal
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
// Base class of all principal JSON parse handlers.
|
||||||
|
class PrincipalJSONHandlerShared : public JS::JSONParseHandler {
|
||||||
|
public:
|
||||||
|
// Common handlers for inner objects.
|
||||||
|
|
||||||
|
// NOTE: propertyName and stringValue are partial overloads.
|
||||||
|
// Subclasses should put the following:
|
||||||
|
// * `using PrincipalJSONHandlerShared::propertyName`
|
||||||
|
// * `using PrincipalJSONHandlerShared::stringValue`
|
||||||
|
virtual bool propertyName(const char16_t* name, size_t length) override {
|
||||||
|
NS_WARNING("Principal JSON shouldn't use non-ASCII");
|
||||||
|
SetErrorState();
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual bool stringValue(const char16_t* str, size_t length) override {
|
||||||
|
NS_WARNING("Principal JSON shouldn't use non-ASCII");
|
||||||
|
SetErrorState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool numberValue(double d) override {
|
||||||
|
NS_WARNING("Unexpected number value");
|
||||||
|
SetErrorState();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool booleanValue(bool v) override {
|
||||||
|
NS_WARNING("Unexpected boolean value");
|
||||||
|
SetErrorState();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool nullValue() override {
|
||||||
|
NS_WARNING("Unexpected null value");
|
||||||
|
SetErrorState();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void error(const char* msg, uint32_t line, uint32_t column) override {
|
||||||
|
// Unused.
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Set to the error state for the above handlers.
|
||||||
|
virtual void SetErrorState() = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RefPtr<BasePrincipal> mPrincipal;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Base class shared between PrincipalJSONHandler and
|
||||||
|
// SubsumedPrincipalJSONHandler.
|
||||||
|
// This implements the common code for them, absorbing the difference about
|
||||||
|
// whether it can contain ExpandedPrincipal or not.
|
||||||
|
template <typename HandlerTypesT>
|
||||||
|
class ContainerPrincipalJSONHandler : public PrincipalJSONHandlerShared {
|
||||||
|
using State = typename HandlerTypesT::State;
|
||||||
|
using InnerHandlerT = typename HandlerTypesT::InnerHandlerT;
|
||||||
|
static constexpr bool CanContainExpandedPrincipal =
|
||||||
|
HandlerTypesT::CanContainExpandedPrincipal;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Common handlers.
|
||||||
|
|
||||||
|
virtual bool startObject() override;
|
||||||
|
|
||||||
|
using PrincipalJSONHandlerShared::propertyName;
|
||||||
|
virtual bool propertyName(const JS::Latin1Char* name, size_t length) override;
|
||||||
|
virtual bool endObject() override;
|
||||||
|
|
||||||
|
virtual bool startArray() override;
|
||||||
|
|
||||||
|
virtual bool endArray() override;
|
||||||
|
|
||||||
|
using PrincipalJSONHandlerShared::stringValue;
|
||||||
|
virtual bool stringValue(const JS::Latin1Char* str, size_t length) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool ProcessInnerResult(bool aResult);
|
||||||
|
|
||||||
|
template <class Func>
|
||||||
|
bool CallOnInner(Func&& aFunc) {
|
||||||
|
return mInnerHandler->match([&](auto& aInner) {
|
||||||
|
bool result = aFunc(aInner);
|
||||||
|
return ProcessInnerResult(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
State mState = State::Init;
|
||||||
|
|
||||||
|
InnerHandlerT mInnerHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_SharedJSONHandler_h
|
||||||
96
caps/SubsumedPrincipalJSONHandler.h
Normal file
96
caps/SubsumedPrincipalJSONHandler.h
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_SubsumedPrincipalJSONHandler_h
|
||||||
|
#define mozilla_SubsumedPrincipalJSONHandler_h
|
||||||
|
|
||||||
|
#include <stddef.h> // size_t
|
||||||
|
#include <stdint.h> // uint32_t
|
||||||
|
|
||||||
|
#include "js/TypeDecls.h" // JS::Latin1Char
|
||||||
|
|
||||||
|
#include "mozilla/AlreadyAddRefed.h" // already_AddRefed
|
||||||
|
#include "mozilla/Variant.h" // Variant
|
||||||
|
#include "mozilla/RefPtr.h" // RefPtr
|
||||||
|
|
||||||
|
#include "nsDebug.h" // NS_WARNING
|
||||||
|
|
||||||
|
#include "BasePrincipal.h"
|
||||||
|
#include "ContentPrincipalJSONHandler.h"
|
||||||
|
#include "NullPrincipalJSONHandler.h"
|
||||||
|
#include "SharedJSONHandler.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
class SubsumedPrincipalJSONHandlerTypes {
|
||||||
|
public:
|
||||||
|
enum class State {
|
||||||
|
Init,
|
||||||
|
|
||||||
|
// After the subsumed principal object's '{'.
|
||||||
|
StartObject,
|
||||||
|
|
||||||
|
// After the PrincipalKind property key for SystemPrincipal.
|
||||||
|
SystemPrincipal_Key,
|
||||||
|
// After the SystemPrincipal's inner object's '{'.
|
||||||
|
SystemPrincipal_StartObject,
|
||||||
|
// After the SystemPrincipal's inner object's '}'.
|
||||||
|
SystemPrincipal_EndObject,
|
||||||
|
|
||||||
|
// After the PrincipalKind property key for NullPrincipal or
|
||||||
|
// ContentPrincipal, and also the entire inner object.
|
||||||
|
// Delegates to mInnerHandler until the inner object's '}'.
|
||||||
|
//
|
||||||
|
// Unlike PrincipalJSONHandler, subsumed principal doesn't contain
|
||||||
|
// ExpandedPrincipal.
|
||||||
|
NullPrincipal_Inner,
|
||||||
|
ContentPrincipal_Inner,
|
||||||
|
|
||||||
|
// After the subsumed principal object's '}'.
|
||||||
|
EndObject,
|
||||||
|
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
using InnerHandlerT =
|
||||||
|
Maybe<Variant<NullPrincipalJSONHandler, ContentPrincipalJSONHandler>>;
|
||||||
|
|
||||||
|
static constexpr bool CanContainExpandedPrincipal = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// JSON parse handler for subsumed principal object inside ExpandedPrincipal's
|
||||||
|
// new format.
|
||||||
|
//
|
||||||
|
// Subsumed principal object
|
||||||
|
// |
|
||||||
|
// ----------------------------------
|
||||||
|
// | |
|
||||||
|
// {"2": {"0": [{"1": {"0": https://mozilla.com"}}]}}
|
||||||
|
// | | |
|
||||||
|
// | ---------------------------
|
||||||
|
// | |
|
||||||
|
// | inner object
|
||||||
|
// PrincipalKind
|
||||||
|
//
|
||||||
|
// For inner object except for the system principal case, this delegates
|
||||||
|
// to NullPrincipalJSONHandler or ContentPrincipalJSONHandler.
|
||||||
|
class SubsumedPrincipalJSONHandler
|
||||||
|
: public ContainerPrincipalJSONHandler<SubsumedPrincipalJSONHandlerTypes> {
|
||||||
|
using State = SubsumedPrincipalJSONHandlerTypes::State;
|
||||||
|
using InnerHandlerT = SubsumedPrincipalJSONHandlerTypes::InnerHandlerT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SubsumedPrincipalJSONHandler() = default;
|
||||||
|
virtual ~SubsumedPrincipalJSONHandler() = default;
|
||||||
|
|
||||||
|
bool HasAccepted() const { return mState == State::EndObject; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void SetErrorState() override { mState = State::Error; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_SubsumedPrincipalJSONHandler_h
|
||||||
@@ -24,10 +24,6 @@
|
|||||||
|
|
||||||
class nsScriptSecurityManager;
|
class nsScriptSecurityManager;
|
||||||
|
|
||||||
namespace Json {
|
|
||||||
class Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
class SystemPrincipal final : public BasePrincipal, public nsISerializable {
|
class SystemPrincipal final : public BasePrincipal, public nsISerializable {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ using mozilla::SystemPrincipal;
|
|||||||
// None of these tests work in debug due to assert guards
|
// None of these tests work in debug due to assert guards
|
||||||
#ifndef MOZ_DEBUG
|
#ifndef MOZ_DEBUG
|
||||||
|
|
||||||
// calling toJson() twice with the same string arg
|
// calling toJSON() twice with the same string arg
|
||||||
// (ensure that we truncate correctly where needed)
|
// (ensure that we truncate correctly where needed)
|
||||||
TEST(PrincipalSerialization, ReusedJSONArgument)
|
TEST(PrincipalSerialization, ReusedJSONArgument)
|
||||||
{
|
{
|
||||||
@@ -46,28 +46,6 @@ TEST(PrincipalSerialization, ReusedJSONArgument)
|
|||||||
ASSERT_TRUE(JSON.EqualsLiteral("{\"1\":{\"0\":\"https://example.com/\"}}"));
|
ASSERT_TRUE(JSON.EqualsLiteral("{\"1\":{\"0\":\"https://example.com/\"}}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assure that calling FromProperties() with an empty array list always returns
|
|
||||||
// a nullptr The exception here is SystemPrincipal which doesn't have fields but
|
|
||||||
// it also doesn't implement FromProperties These are overly cautious checks
|
|
||||||
// that we don't try to create a principal in reality FromProperties is only
|
|
||||||
// called with a populated array.
|
|
||||||
TEST(PrincipalSerialization, FromPropertiesEmpty)
|
|
||||||
{
|
|
||||||
nsTArray<ContentPrincipal::KeyVal> resContent;
|
|
||||||
nsCOMPtr<nsIPrincipal> contentPrincipal =
|
|
||||||
ContentPrincipal::FromProperties(resContent);
|
|
||||||
ASSERT_EQ(nullptr, contentPrincipal);
|
|
||||||
|
|
||||||
nsTArray<ExpandedPrincipal::KeyVal> resExpanded;
|
|
||||||
nsCOMPtr<nsIPrincipal> expandedPrincipal =
|
|
||||||
ExpandedPrincipal::FromProperties(resExpanded);
|
|
||||||
ASSERT_EQ(nullptr, expandedPrincipal);
|
|
||||||
|
|
||||||
nsTArray<NullPrincipal::KeyVal> resNull;
|
|
||||||
nsCOMPtr<nsIPrincipal> nullprincipal = NullPrincipal::FromProperties(resNull);
|
|
||||||
ASSERT_EQ(nullptr, nullprincipal);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Double check that if we have two valid principals in a serialized JSON that
|
// Double check that if we have two valid principals in a serialized JSON that
|
||||||
// nullptr is returned
|
// nullptr is returned
|
||||||
TEST(PrincipalSerialization, TwoKeys)
|
TEST(PrincipalSerialization, TwoKeys)
|
||||||
|
|||||||
Reference in New Issue
Block a user