Bug 1945576, part 2 - Add implementation for Login Status JS hooks - r=anti-tracking-reviewers,timhuang

Differential Revision: https://phabricator.services.mozilla.com/D240218
This commit is contained in:
Benjamin VanderSloot
2025-03-26 18:09:51 +00:00
parent f690d4bf31
commit d7866d603a
8 changed files with 156 additions and 13 deletions

View File

@@ -98,7 +98,9 @@ static bool ConsumeUserActivation(nsPIDOMWindowInner* aParent) {
return doc->ConsumeTransientUserGestureActivation();
}
static bool IsSameOriginWithAncestors(nsPIDOMWindowInner* aParent) {
// static
bool CredentialsContainer::IsSameOriginWithAncestors(
nsPIDOMWindowInner* aParent) {
// This method returns true if aParent is either not in a frame / iframe, or
// is in a frame or iframe and all ancestors for aParent are the same origin.
// This is useful for Credential Management because we need to prohibit

View File

@@ -38,6 +38,8 @@ class CredentialsContainer final : public nsISupports, public nsWrapperCache {
already_AddRefed<Promise> PreventSilentAccess(ErrorResult& aRv);
static bool IsSameOriginWithAncestors(nsPIDOMWindowInner* aParent);
private:
~CredentialsContainer();

View File

@@ -11,6 +11,7 @@
#include "mozilla/dom/IdentityCredential.h"
#include "mozilla/dom/IdentityCredentialBinding.h"
#include "mozilla/dom/CredentialManagementBinding.h"
#include "mozilla/dom/LoginStatusBinding.h"
namespace IPC {
@@ -81,6 +82,10 @@ struct ParamTraits<mozilla::dom::IdentityCredentialRequestOptions> {
}
};
template <>
struct ParamTraits<mozilla::dom::LoginStatus>
: public mozilla::dom::WebIDLEnumSerializer<mozilla::dom::LoginStatus> {};
} // namespace IPC
#endif // mozilla_dom_identitycredentialserializationhelpers_h__

View File

@@ -4,10 +4,48 @@
* 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/. */
#include "mozilla/Components.h"
#include "mozilla/dom/CredentialsContainer.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/NavigatorLogin.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/Maybe.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIGlobalObject.h"
#include "nsIPermissionManager.h"
#include "nsIPrincipal.h"
#include "nsString.h"
namespace mozilla::dom {
namespace mozilla {
namespace dom {
static constexpr nsLiteralCString kLoginStatusPermission =
"self-reported-logged-in"_ns;
uint32_t ConvertStatusToPermission(LoginStatus aStatus) {
if (aStatus == LoginStatus::Logged_in) {
return nsIPermissionManager::ALLOW_ACTION;
} else if (aStatus == LoginStatus::Logged_out) {
return nsIPermissionManager::DENY_ACTION;
} else {
// This should be unreachable, but let's return a real value
return nsIPermissionManager::UNKNOWN_ACTION;
}
}
Maybe<LoginStatus> PermissionToStatus(uint32_t aPermission) {
if (aPermission == nsIPermissionManager::ALLOW_ACTION) {
return Some(LoginStatus::Logged_in);
} else if (aPermission == nsIPermissionManager::DENY_ACTION) {
return Some(LoginStatus::Logged_out);
} else {
if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) {
MOZ_ASSERT(false, "Unexpected permission action from login status");
}
return Nothing();
}
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(NavigatorLogin, mOwner)
@@ -18,21 +56,91 @@ JSObject* NavigatorLogin::WrapObject(JSContext* aCx,
return NavigatorLogin_Binding::Wrap(aCx, this, aGivenProto);
}
NavigatorLogin::NavigatorLogin(nsIGlobalObject* aGlobal)
: mOwner(aGlobal){
MOZ_ASSERT(mOwner);
};
NavigatorLogin::NavigatorLogin(nsIGlobalObject* aGlobal) : mOwner(aGlobal) {
MOZ_ASSERT(mOwner);
};
already_AddRefed<mozilla::dom::Promise> NavigatorLogin::SetStatus(
const LoginStatus& aStatus, mozilla::ErrorResult& aRv) {
LoginStatus aStatus, mozilla::ErrorResult& aRv) {
RefPtr<Promise> promise = Promise::Create(mOwner, aRv);
if (aRv.Failed()) {
return nullptr;
}
promise->MaybeRejectWithNotSupportedError(
"navigator.login.setStatus not implemented"_ns);
nsPIDOMWindowInner* window = mOwner->GetAsInnerWindow();
if (!window) {
promise->MaybeRejectWithUnknownError(
"navigator.login.setStatus called on unavailable window"_ns);
return promise.forget();
}
if (!CredentialsContainer::IsSameOriginWithAncestors(window)) {
promise->MaybeRejectWithSecurityError(
"navigator.login.setStatus must be called in a frame that is same-origin with its ancestors"_ns);
return promise.forget();
}
WindowGlobalChild* wgc = window->GetWindowGlobalChild();
if (!wgc) {
promise->MaybeRejectWithUnknownError(
"navigator.login.setStatus called while window already destroyed"_ns);
return promise.forget();
}
wgc->SendSetLoginStatus(aStatus)->Then(
GetCurrentSerialEventTarget(), __func__,
[promise](
const WindowGlobalChild::SetLoginStatusPromise::ResolveValueType&
aResult) {
if (NS_SUCCEEDED(aResult)) {
promise->MaybeResolveWithUndefined();
} else {
promise->MaybeRejectWithUnknownError(
"navigator.login.setStatus had an unexpected internal error");
}
},
[promise](const WindowGlobalChild::SetLoginStatusPromise::RejectValueType&
aResult) {
promise->MaybeRejectWithUnknownError(
"navigator.login.setStatus had an unexpected internal error");
});
return promise.forget();
}
} // namespace mozilla::dom
// static
nsresult NavigatorLogin::SetLoginStatus(nsIPrincipal* aPrincipal,
LoginStatus aStatus) {
MOZ_ASSERT(XRE_IsParentProcess());
nsCOMPtr<nsIPermissionManager> permMgr =
components::PermissionManager::Service();
if (!permMgr) {
return NS_ERROR_NOT_AVAILABLE;
}
return permMgr->AddFromPrincipal(aPrincipal, kLoginStatusPermission,
ConvertStatusToPermission(aStatus),
nsIPermissionManager::EXPIRE_NEVER, 0);
}
// static
nsresult GetLoginStatus(nsIPrincipal* aPrincipal, Maybe<LoginStatus>& aStatus) {
nsCOMPtr<nsIPermissionManager> permMgr =
components::PermissionManager::Service();
if (!permMgr) {
aStatus = Nothing();
return NS_ERROR_SERVICE_NOT_AVAILABLE;
}
uint32_t action;
nsresult rv = permMgr->TestPermissionFromPrincipal(
aPrincipal, kLoginStatusPermission, &action);
if (NS_FAILED(rv)) {
aStatus = Nothing();
return rv;
}
aStatus = PermissionToStatus(action);
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@@ -7,7 +7,10 @@
#ifndef mozilla_dom_NavigatorLogin_h
#define mozilla_dom_NavigatorLogin_h
#include "ErrorList.h"
#include "mozilla/dom/LoginStatusBinding.h"
#include "mozilla/Maybe.h"
#include "nsIGlobalObject.h"
#include "nsISupports.h"
#include "nsWrapperCache.h"
#include "nsCOMPtr.h"
@@ -24,9 +27,12 @@ class NavigatorLogin : public nsWrapperCache {
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
already_AddRefed<mozilla::dom::Promise> SetStatus(const LoginStatus& aStatus,
already_AddRefed<mozilla::dom::Promise> SetStatus(LoginStatus aStatus,
mozilla::ErrorResult& aRv);
static Maybe<LoginStatus> GetLoginStatus(nsIPrincipal* aPrincipal);
static nsresult SetLoginStatus(nsIPrincipal* aPrincipal, LoginStatus aStatus);
protected:
virtual ~NavigatorLogin();
@@ -36,4 +42,4 @@ class NavigatorLogin : public nsWrapperCache {
} // namespace mozilla::dom
#endif // mozilla_dom_NavigatorLogin_h
#endif // mozilla_dom_NavigatorLogin_h

View File

@@ -40,6 +40,7 @@ using mozilla::dom::IdentityCredentialDisconnectOptions from "mozilla/dom/Identi
using mozilla::dom::IdentityCredentialInit from "mozilla/dom/IdentityCredentialBinding.h";
using mozilla::dom::IdentityCredentialRequestOptions from "mozilla/dom/IdentityCredentialBinding.h";
using mozilla::dom::IdentityLoginTargetType from "mozilla/dom/IdentityCredentialBinding.h";
using mozilla::dom::LoginStatus from "mozilla/dom/LoginStatusBinding.h";
namespace mozilla {
namespace dom {
@@ -218,6 +219,9 @@ parent:
async DisconnectIdentityCredential(IdentityCredentialDisconnectOptions aOptions) returns (nsresult rv);
async PreventSilentAccess() returns (nsresult rv);
// Indicate that the page has reported itself as logged in
async SetLoginStatus(LoginStatus foo) returns (nsresult rv);
async GetStorageAccessPermission(bool aIncludeIdentityCredential) returns(uint32_t permission_action);

View File

@@ -26,6 +26,7 @@
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/IdentityCredential.h"
#include "mozilla/dom/MediaController.h"
#include "mozilla/dom/NavigatorLogin.h"
#include "mozilla/dom/WebAuthnTransactionParent.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/ChromeUtils.h"
@@ -1503,6 +1504,18 @@ IPCResult WindowGlobalParent::RecvPreventSilentAccess(
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvSetLoginStatus(
LoginStatus aStatus, const SetLoginStatusResolver& aResolver) {
nsIPrincipal* principal = DocumentPrincipal();
if (!principal) {
aResolver(NS_ERROR_DOM_NOT_ALLOWED_ERR);
return IPC_OK();
}
nsresult rv = NavigatorLogin::SetLoginStatus(principal, aStatus);
aResolver(rv);
return IPC_OK();
}
IPCResult WindowGlobalParent::RecvGetStorageAccessPermission(
bool aIncludeIdentityCredential,
GetStorageAccessPermissionResolver&& aResolve) {

View File

@@ -328,6 +328,9 @@ class WindowGlobalParent final : public WindowContext,
mozilla::ipc::IPCResult RecvDisconnectIdentityCredential(
const IdentityCredentialDisconnectOptions& aOptions,
const DisconnectIdentityCredentialResolver& aResolver);
mozilla::ipc::IPCResult RecvSetLoginStatus(
LoginStatus aStatus, const SetLoginStatusResolver& aResolver);
mozilla::ipc::IPCResult RecvPreventSilentAccess(
const PreventSilentAccessResolver& aResolver);