Bug 1675375 Part 6: Break test_group_hittest.html into parts. r=botond
[gecko.git] / caps / ExpandedPrincipal.cpp
blobfe70c71a728e5f4aaf6809449caf4b31185576d7
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ExpandedPrincipal.h"
8 #include "nsIClassInfoImpl.h"
9 #include "nsReadableUtils.h"
10 #include "mozilla/Base64.h"
12 using namespace mozilla;
14 NS_IMPL_CLASSINFO(ExpandedPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
15 NS_EXPANDEDPRINCIPAL_CID)
16 NS_IMPL_QUERY_INTERFACE_CI(ExpandedPrincipal, nsIPrincipal,
17 nsIExpandedPrincipal, nsISerializable)
18 NS_IMPL_CI_INTERFACE_GETTER(ExpandedPrincipal, nsIPrincipal,
19 nsIExpandedPrincipal, nsISerializable)
21 struct OriginComparator {
22 bool LessThan(nsIPrincipal* a, nsIPrincipal* b) const {
23 nsAutoCString originA;
24 DebugOnly<nsresult> rv = a->GetOrigin(originA);
25 MOZ_ASSERT(NS_SUCCEEDED(rv));
26 nsAutoCString originB;
27 rv = b->GetOrigin(originB);
28 MOZ_ASSERT(NS_SUCCEEDED(rv));
29 return originA < originB;
32 bool Equals(nsIPrincipal* a, nsIPrincipal* b) const {
33 nsAutoCString originA;
34 DebugOnly<nsresult> rv = a->GetOrigin(originA);
35 MOZ_ASSERT(NS_SUCCEEDED(rv));
36 nsAutoCString originB;
37 rv = b->GetOrigin(originB);
38 MOZ_ASSERT(NS_SUCCEEDED(rv));
39 return a == b;
43 ExpandedPrincipal::ExpandedPrincipal(
44 nsTArray<nsCOMPtr<nsIPrincipal>>& aAllowList)
45 : BasePrincipal(eExpandedPrincipal) {
46 // We force the principals to be sorted by origin so that ExpandedPrincipal
47 // origins can have a canonical form.
48 OriginComparator c;
49 for (size_t i = 0; i < aAllowList.Length(); ++i) {
50 mPrincipals.InsertElementSorted(aAllowList[i], c);
54 ExpandedPrincipal::ExpandedPrincipal() : BasePrincipal(eExpandedPrincipal) {}
56 ExpandedPrincipal::~ExpandedPrincipal() {}
58 already_AddRefed<ExpandedPrincipal> ExpandedPrincipal::Create(
59 nsTArray<nsCOMPtr<nsIPrincipal>>& aAllowList,
60 const OriginAttributes& aAttrs) {
61 RefPtr<ExpandedPrincipal> ep = new ExpandedPrincipal(aAllowList);
63 nsAutoCString origin;
64 origin.AssignLiteral("[Expanded Principal [");
65 StringJoinAppend(
66 origin, ", "_ns, ep->mPrincipals,
67 [](nsACString& dest, const nsCOMPtr<nsIPrincipal>& principal) {
68 nsAutoCString subOrigin;
69 DebugOnly<nsresult> rv = principal->GetOrigin(subOrigin);
70 MOZ_ASSERT(NS_SUCCEEDED(rv));
71 dest.Append(subOrigin);
72 });
73 origin.AppendLiteral("]]");
75 ep->FinishInit(origin, aAttrs);
76 return ep.forget();
79 NS_IMETHODIMP
80 ExpandedPrincipal::GetDomain(nsIURI** aDomain) {
81 *aDomain = nullptr;
82 return NS_OK;
85 NS_IMETHODIMP
86 ExpandedPrincipal::SetDomain(nsIURI* aDomain) { return NS_OK; }
88 bool ExpandedPrincipal::SubsumesInternal(
89 nsIPrincipal* aOther,
90 BasePrincipal::DocumentDomainConsideration aConsideration) {
91 // If aOther is an ExpandedPrincipal too, we break it down into its component
92 // nsIPrincipals, and check subsumes on each one.
93 if (Cast(aOther)->Is<ExpandedPrincipal>()) {
94 auto* expanded = Cast(aOther)->As<ExpandedPrincipal>();
96 for (auto& other : expanded->AllowList()) {
97 // Use SubsumesInternal rather than Subsumes here, since OriginAttribute
98 // checks are only done between non-expanded sub-principals, and we don't
99 // need to incur the extra virtual call overhead.
100 if (!SubsumesInternal(other, aConsideration)) {
101 return false;
104 return true;
107 // We're dealing with a regular principal. One of our principals must subsume
108 // it.
109 for (uint32_t i = 0; i < mPrincipals.Length(); ++i) {
110 if (Cast(mPrincipals[i])->Subsumes(aOther, aConsideration)) {
111 return true;
115 return false;
118 bool ExpandedPrincipal::MayLoadInternal(nsIURI* uri) {
119 for (uint32_t i = 0; i < mPrincipals.Length(); ++i) {
120 if (BasePrincipal::Cast(mPrincipals[i])->MayLoadInternal(uri)) {
121 return true;
125 return false;
128 uint32_t ExpandedPrincipal::GetHashValue() {
129 MOZ_CRASH("extended principal should never be used as key in a hash map");
132 NS_IMETHODIMP
133 ExpandedPrincipal::GetURI(nsIURI** aURI) {
134 *aURI = nullptr;
135 return NS_OK;
138 const nsTArray<nsCOMPtr<nsIPrincipal>>& ExpandedPrincipal::AllowList() {
139 return mPrincipals;
142 NS_IMETHODIMP
143 ExpandedPrincipal::GetBaseDomain(nsACString& aBaseDomain) {
144 return NS_ERROR_NOT_AVAILABLE;
147 NS_IMETHODIMP
148 ExpandedPrincipal::GetAddonId(nsAString& aAddonId) {
149 aAddonId.Truncate();
150 return NS_OK;
153 bool ExpandedPrincipal::AddonHasPermission(const nsAtom* aPerm) {
154 for (size_t i = 0; i < mPrincipals.Length(); ++i) {
155 if (BasePrincipal::Cast(mPrincipals[i])->AddonHasPermission(aPerm)) {
156 return true;
159 return false;
162 bool ExpandedPrincipal::AddonAllowsLoad(nsIURI* aURI,
163 bool aExplicit /* = false */) {
164 for (const auto& principal : mPrincipals) {
165 if (Cast(principal)->AddonAllowsLoad(aURI, aExplicit)) {
166 return true;
169 return false;
172 void ExpandedPrincipal::SetCsp(nsIContentSecurityPolicy* aCSP) { mCSP = aCSP; }
174 NS_IMETHODIMP
175 ExpandedPrincipal::GetCsp(nsIContentSecurityPolicy** aCsp) {
176 NS_IF_ADDREF(*aCsp = mCSP);
177 return NS_OK;
180 nsIPrincipal* ExpandedPrincipal::PrincipalToInherit(nsIURI* aRequestedURI) {
181 if (aRequestedURI) {
182 // If a given sub-principal subsumes the given URI, use that principal for
183 // inheritance. In general, this only happens with certain CORS modes, loads
184 // with forced principal inheritance, and creation of XML documents from
185 // XMLHttpRequests or fetch requests. For URIs that normally inherit a
186 // principal (such as data: URIs), we fall back to the last principal in the
187 // allowlist.
188 for (const auto& principal : mPrincipals) {
189 if (Cast(principal)->MayLoadInternal(aRequestedURI)) {
190 return principal;
194 return mPrincipals.LastElement();
197 nsresult ExpandedPrincipal::GetScriptLocation(nsACString& aStr) {
198 aStr.AssignLiteral("[Expanded Principal [");
199 for (size_t i = 0; i < mPrincipals.Length(); ++i) {
200 if (i != 0) {
201 aStr.AppendLiteral(", ");
204 nsAutoCString spec;
205 nsresult rv =
206 nsJSPrincipals::get(mPrincipals.ElementAt(i))->GetScriptLocation(spec);
207 NS_ENSURE_SUCCESS(rv, rv);
209 aStr.Append(spec);
211 aStr.AppendLiteral("]]");
212 return NS_OK;
215 //////////////////////////////////////////
216 // Methods implementing nsISerializable //
217 //////////////////////////////////////////
219 // We've had way too many issues with unversioned serializations, so
220 // explicitly version this one.
221 static const uint32_t kSerializationVersion = 1;
223 NS_IMETHODIMP
224 ExpandedPrincipal::Read(nsIObjectInputStream* aStream) {
225 uint32_t version;
226 nsresult rv = aStream->Read32(&version);
227 if (version != kSerializationVersion) {
228 MOZ_ASSERT(false,
229 "We really need to add handling of the old(?) version here");
230 return NS_ERROR_UNEXPECTED;
233 uint32_t count;
234 rv = aStream->Read32(&count);
235 if (NS_FAILED(rv)) {
236 return rv;
239 if (!mPrincipals.SetCapacity(count, fallible)) {
240 return NS_ERROR_OUT_OF_MEMORY;
243 OriginComparator c;
244 for (uint32_t i = 0; i < count; ++i) {
245 nsCOMPtr<nsISupports> read;
246 rv = aStream->ReadObject(true, getter_AddRefs(read));
247 if (NS_FAILED(rv)) {
248 return rv;
251 nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(read);
252 if (!principal) {
253 return NS_ERROR_UNEXPECTED;
256 // Play it safe and InsertElementSorted, in case the sort order
257 // changed for some bizarre reason.
258 mPrincipals.InsertElementSorted(std::move(principal), c);
261 return NS_OK;
264 NS_IMETHODIMP
265 ExpandedPrincipal::Write(nsIObjectOutputStream* aStream) {
266 // Read is used still for legacy principals
267 MOZ_RELEASE_ASSERT(false, "Old style serialization is removed");
268 return NS_OK;
271 nsresult ExpandedPrincipal::GetSiteIdentifier(SiteIdentifier& aSite) {
272 // Call GetSiteIdentifier on each of our principals and return a new
273 // ExpandedPrincipal.
275 nsTArray<nsCOMPtr<nsIPrincipal>> allowlist;
276 for (const auto& principal : mPrincipals) {
277 SiteIdentifier site;
278 nsresult rv = Cast(principal)->GetSiteIdentifier(site);
279 NS_ENSURE_SUCCESS(rv, rv);
280 allowlist.AppendElement(site.GetPrincipal());
283 RefPtr<ExpandedPrincipal> expandedPrincipal =
284 ExpandedPrincipal::Create(allowlist, OriginAttributesRef());
285 MOZ_ASSERT(expandedPrincipal, "ExpandedPrincipal::Create returned nullptr?");
287 aSite.Init(expandedPrincipal);
288 return NS_OK;
291 nsresult ExpandedPrincipal::PopulateJSONObject(Json::Value& aObject) {
292 nsAutoCString principalList;
293 // First item through we have a blank separator and append the next result
294 nsAutoCString sep;
295 for (auto& principal : mPrincipals) {
296 nsAutoCString JSON;
297 BasePrincipal::Cast(principal)->ToJSON(JSON);
298 // This is blank for the first run through so the last in the list doesn't
299 // add a separator
300 principalList.Append(sep);
301 sep = ',';
302 // Values currently only copes with strings so encode into base64 to allow a
303 // CSV safely.
304 nsresult rv;
305 rv = Base64EncodeAppend(JSON, principalList);
306 NS_ENSURE_SUCCESS(rv, rv);
308 aObject[std::to_string(eSpecs)] = principalList.get();
310 nsAutoCString suffix;
311 OriginAttributesRef().CreateSuffix(suffix);
312 if (suffix.Length() > 0) {
313 aObject[std::to_string(eSuffix)] = suffix.get();
316 return NS_OK;
319 already_AddRefed<BasePrincipal> ExpandedPrincipal::FromProperties(
320 nsTArray<ExpandedPrincipal::KeyVal>& aFields) {
321 MOZ_ASSERT(aFields.Length() == eMax + 1, "Must have all the keys");
322 nsTArray<nsCOMPtr<nsIPrincipal>> allowList;
323 OriginAttributes attrs;
324 // The odd structure here is to make the code to not compile
325 // if all the switch enum cases haven't been codified
326 for (const auto& field : aFields) {
327 switch (field.key) {
328 case ExpandedPrincipal::eSpecs:
329 if (!field.valueWasSerialized) {
330 MOZ_ASSERT(false,
331 "Expanded principals require specs in serialized JSON");
332 return nullptr;
334 for (const nsACString& each : field.value.Split(',')) {
335 nsAutoCString result;
336 nsresult rv;
337 rv = Base64Decode(each, result);
338 MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to decode");
340 NS_ENSURE_SUCCESS(rv, nullptr);
341 nsCOMPtr<nsIPrincipal> principal = BasePrincipal::FromJSON(result);
342 allowList.AppendElement(principal);
344 break;
345 case ExpandedPrincipal::eSuffix:
346 if (field.valueWasSerialized) {
347 bool ok = attrs.PopulateFromSuffix(field.value);
348 if (!ok) {
349 return nullptr;
352 break;
356 if (allowList.Length() == 0) {
357 return nullptr;
360 RefPtr<ExpandedPrincipal> expandedPrincipal =
361 ExpandedPrincipal::Create(allowList, attrs);
363 return expandedPrincipal.forget();
366 NS_IMETHODIMP
367 ExpandedPrincipal::IsThirdPartyURI(nsIURI* aURI, bool* aRes) {
368 // ExpandedPrincipal for extension content scripts consist of two principals,
369 // the document's principal and the extension's principal.
370 // To make sure that the third-party check behaves like the web page on which
371 // the content script is running, ignore the extension's principal.
373 for (const auto& principal : mPrincipals) {
374 if (!Cast(principal)->AddonPolicy()) {
375 return Cast(principal)->IsThirdPartyURI(aURI, aRes);
379 if (mPrincipals.IsEmpty()) {
380 *aRes = true;
381 return NS_OK;
384 return Cast(mPrincipals[0])->IsThirdPartyURI(aURI, aRes);