Diff
Modified: trunk/Source/WebCore/ChangeLog (253488 => 253489)
--- trunk/Source/WebCore/ChangeLog 2019-12-13 19:07:51 UTC (rev 253488)
+++ trunk/Source/WebCore/ChangeLog 2019-12-13 19:18:37 UTC (rev 253489)
@@ -1,3 +1,28 @@
+2019-12-13 John Wilander <[email protected]>
+
+ IsLoggedIn: Abstract data type for IsLoggedIn state
+ https://bugs.webkit.org/show_bug.cgi?id=205041
+ <rdar://problem/56723904>
+
+ Reviewed by Chris Dumez.
+
+ New API tests added.
+
+ * Sources.txt:
+ * WebCore.xcodeproj/project.pbxproj:
+ * page/LoggedInStatus.cpp: Added.
+ (WebCore::LoggedInStatus::create):
+ (WebCore::LoggedInStatus::LoggedInStatus):
+ (WebCore::LoggedInStatus::setTimeToLive):
+ (WebCore::LoggedInStatus::hasExpired const):
+ (WebCore::LoggedInStatus::expiry const):
+ * page/LoggedInStatus.h: Added.
+ (WebCore::LoggedInStatus::registrableDomain const):
+ (WebCore::LoggedInStatus::username const):
+ (WebCore::LoggedInStatus::credentialTokenType const):
+ (WebCore::LoggedInStatus::authenticationType const):
+ (WebCore::LoggedInStatus::loggedInTime const):
+
2019-12-13 Per Arne Vollan <[email protected]>
[iOS] Deny mach lookup access to "*.apple-extension-service" in the WebContent process
Modified: trunk/Source/WebCore/Sources.txt (253488 => 253489)
--- trunk/Source/WebCore/Sources.txt 2019-12-13 19:07:51 UTC (rev 253488)
+++ trunk/Source/WebCore/Sources.txt 2019-12-13 19:18:37 UTC (rev 253489)
@@ -1608,6 +1608,7 @@
page/History.cpp
page/IntersectionObserver.cpp
page/IntersectionObserverEntry.cpp
+page/LoggedInStatus.cpp
page/Location.cpp
page/MemoryRelease.cpp
page/MouseEventWithHitTestResults.cpp
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (253488 => 253489)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2019-12-13 19:07:51 UTC (rev 253488)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2019-12-13 19:18:37 UTC (rev 253489)
@@ -1991,6 +1991,7 @@
6B0A07F221FA4B5C00D57391 /* AdClickAttribution.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B0A07F021FA4B5C00D57391 /* AdClickAttribution.h */; settings = {ATTRIBUTES = (Private, ); }; };
6B1F48112298A37E00DE8B82 /* CrossSiteNavigationDataTransfer.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B1F480F22989EC400DE8B82 /* CrossSiteNavigationDataTransfer.h */; settings = {ATTRIBUTES = (Private, ); }; };
6B3480940EEF50D400AC1B41 /* NativeImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B3480920EEF50D400AC1B41 /* NativeImage.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 6B4D412D23983F88002494C2 /* LoggedInStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B4D412B23983F88002494C2 /* LoggedInStatus.h */; settings = {ATTRIBUTES = (Private, ); }; };
6B4E8613221B713F0022F389 /* RegistrableDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B4E8612221B713F0022F389 /* RegistrableDomain.h */; settings = {ATTRIBUTES = (Private, ); }; };
6B507A24234BF34100BE7C62 /* NavigatorIsLoggedIn.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B507A21234BF34100BE7C62 /* NavigatorIsLoggedIn.h */; };
6B693A2E1C51A82E00B03BEF /* ResourceLoadObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B693A2D1C51A82E00B03BEF /* ResourceLoadObserver.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -9210,6 +9211,8 @@
6B0A07F121FA4B5C00D57391 /* AdClickAttribution.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AdClickAttribution.cpp; sourceTree = "<group>"; };
6B1F480F22989EC400DE8B82 /* CrossSiteNavigationDataTransfer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CrossSiteNavigationDataTransfer.h; sourceTree = "<group>"; };
6B3480920EEF50D400AC1B41 /* NativeImage.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NativeImage.h; sourceTree = "<group>"; };
+ 6B4D412B23983F88002494C2 /* LoggedInStatus.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoggedInStatus.h; sourceTree = "<group>"; };
+ 6B4D412C23983F88002494C2 /* LoggedInStatus.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LoggedInStatus.cpp; sourceTree = "<group>"; };
6B4E8612221B713F0022F389 /* RegistrableDomain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RegistrableDomain.h; sourceTree = "<group>"; };
6B507A21234BF34100BE7C62 /* NavigatorIsLoggedIn.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NavigatorIsLoggedIn.h; sourceTree = "<group>"; };
6B507A22234BF34100BE7C62 /* NavigatorIsLoggedIn.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NavigatorIsLoggedIn.cpp; sourceTree = "<group>"; };
@@ -20672,6 +20675,8 @@
BCE1C41A0D982980003B02F2 /* Location.cpp */,
BCE1C4190D982980003B02F2 /* Location.h */,
BCE1C4220D9829F2003B02F2 /* Location.idl */,
+ 6B4D412C23983F88002494C2 /* LoggedInStatus.cpp */,
+ 6B4D412B23983F88002494C2 /* LoggedInStatus.h */,
931BCC601124DFCB00BE70DD /* MediaCanStartListener.h */,
52E2CAFB19FF0207001EEB4F /* MediaProducer.h */,
413E00771DB0E4DE002341D2 /* MemoryRelease.cpp */,
@@ -31333,6 +31338,7 @@
A516E8B7136E04DB0076C3C0 /* LocalizedDateCache.h in Headers */,
935207BE09BD410A00F2038D /* LocalizedStrings.h in Headers */,
BCE1C41B0D982980003B02F2 /* Location.h in Headers */,
+ 6B4D412D23983F88002494C2 /* LoggedInStatus.h in Headers */,
A8239E0109B3CF8A00B60641 /* Logging.h in Headers */,
9BA273F4172206BB0097CE47 /* LogicalSelectionOffsetCaches.h in Headers */,
0FDCD7F31D47E655009F08BC /* LogInitialization.h in Headers */,
Added: trunk/Source/WebCore/page/LoggedInStatus.cpp (0 => 253489)
--- trunk/Source/WebCore/page/LoggedInStatus.cpp (rev 0)
+++ trunk/Source/WebCore/page/LoggedInStatus.cpp 2019-12-13 19:18:37 UTC (rev 253489)
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "LoggedInStatus.h"
+
+#include <wtf/IsoMallocInlines.h>
+#include <wtf/text/StringCommon.h>
+
+namespace WebCore {
+
+using CodeUnitMatchFunction = bool (*)(UChar);
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(LoggedInStatus);
+
+ExceptionOr<UniqueRef<LoggedInStatus>> LoggedInStatus::create(const RegistrableDomain& domain, const String& username, CredentialTokenType tokenType, AuthenticationType authType)
+{
+ return create(domain, username, tokenType, authType, authType == AuthenticationType::Unmanaged ? TimeToLiveShort : TimeToLiveLong);
+}
+
+ExceptionOr<UniqueRef<LoggedInStatus>> LoggedInStatus::create(const RegistrableDomain& domain, const String& username, CredentialTokenType tokenType, AuthenticationType authType, Seconds timeToLive)
+{
+ if (domain.isEmpty())
+ return Exception { SecurityError, "IsLoggedIn status can only be set for origins with a registrable domain." };
+
+ if (username.isEmpty())
+ return Exception { SyntaxError, "IsLoggedIn requires a non-empty username." };
+
+ unsigned length = username.length();
+ if (length > UsernameMaxLength)
+ return Exception { SyntaxError, makeString("IsLoggedIn usernames cannot be longer than ", UsernameMaxLength) };
+
+ auto spaceOrNewline = username.find([](UChar ch) {
+ return isSpaceOrNewline(ch);
+ });
+ if (spaceOrNewline != notFound)
+ return Exception { InvalidCharacterError, "IsLoggedIn usernames cannot contain whitespace or newlines." };
+
+ return makeUniqueRef<LoggedInStatus>(*new LoggedInStatus(domain, username, tokenType, authType, timeToLive));
+}
+
+LoggedInStatus::LoggedInStatus(const RegistrableDomain& domain, const String& username, CredentialTokenType tokenType, AuthenticationType authType, Seconds timeToLive)
+ : m_domain { domain }
+ , m_username { username }
+ , m_tokenType { tokenType }
+ , m_authType { authType }
+ , m_loggedInTime { WallTime::now() }
+{
+ setTimeToLive(timeToLive);
+}
+
+void LoggedInStatus::setTimeToLive(Seconds timeToLive)
+{
+ m_timeToLive = std::min(timeToLive, m_authType == AuthenticationType::Unmanaged ? TimeToLiveShort : TimeToLiveLong);
+}
+
+bool LoggedInStatus::hasExpired() const
+{
+ ASSERT(!m_domain.isEmpty() && !m_username.isEmpty());
+ return WallTime::now() > m_loggedInTime + m_timeToLive;
+}
+
+WallTime LoggedInStatus::expiry() const
+{
+ return WallTime::now() + m_timeToLive;
+}
+
+}
Added: trunk/Source/WebCore/page/LoggedInStatus.h (0 => 253489)
--- trunk/Source/WebCore/page/LoggedInStatus.h (rev 0)
+++ trunk/Source/WebCore/page/LoggedInStatus.h 2019-12-13 19:18:37 UTC (rev 253489)
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ExceptionOr.h"
+#include "RegistrableDomain.h"
+#include <wtf/EnumTraits.h>
+#include <wtf/IsoMalloc.h>
+#include <wtf/UniqueRef.h>
+#include <wtf/WallTime.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class LoggedInStatus {
+ WTF_MAKE_ISO_ALLOCATED_EXPORT(LoggedInStatus, WEBCORE_EXPORT);
+public:
+ static constexpr uint32_t UsernameMaxLength = 64;
+ static constexpr Seconds TimeToLiveShort { 24_h * 7 };
+ static constexpr Seconds TimeToLiveLong { 24_h * 90 };
+
+ enum class CredentialTokenType : bool { LegacyCookie, HTTPStateToken };
+ enum class AuthenticationType : uint8_t { WebAuthn, PasswordManager, Unmanaged };
+
+ WEBCORE_EXPORT static ExceptionOr<UniqueRef<LoggedInStatus>> create(const RegistrableDomain&, const String& username, CredentialTokenType, AuthenticationType);
+ WEBCORE_EXPORT static ExceptionOr<UniqueRef<LoggedInStatus>> create(const RegistrableDomain&, const String& username, CredentialTokenType, AuthenticationType, Seconds timeToLive);
+
+ WEBCORE_EXPORT void setTimeToLive(Seconds);
+ WEBCORE_EXPORT bool hasExpired() const;
+ WEBCORE_EXPORT WallTime expiry() const;
+
+ const RegistrableDomain& registrableDomain() const { return m_domain; }
+ const String& username() const { return m_username; }
+ CredentialTokenType credentialTokenType() const { return m_tokenType; }
+ AuthenticationType authenticationType() const { return m_authType; }
+ WallTime loggedInTime() const { return m_loggedInTime; }
+
+private:
+ LoggedInStatus(const RegistrableDomain&, const String& username, CredentialTokenType, AuthenticationType, Seconds timeToLive);
+
+ RegistrableDomain m_domain;
+ String m_username;
+ CredentialTokenType m_tokenType;
+ AuthenticationType m_authType;
+ WallTime m_loggedInTime;
+ Seconds m_timeToLive;
+};
+
+} // namespace WebCore
+
+namespace WTF {
+
+template<> struct EnumTraits<WebCore::LoggedInStatus::AuthenticationType> {
+ using values = EnumValues<
+ WebCore::LoggedInStatus::AuthenticationType,
+ WebCore::LoggedInStatus::AuthenticationType::WebAuthn,
+ WebCore::LoggedInStatus::AuthenticationType::PasswordManager,
+ WebCore::LoggedInStatus::AuthenticationType::Unmanaged
+ >;
+};
+
+} // namespace WTF
Modified: trunk/Tools/ChangeLog (253488 => 253489)
--- trunk/Tools/ChangeLog 2019-12-13 19:07:51 UTC (rev 253488)
+++ trunk/Tools/ChangeLog 2019-12-13 19:18:37 UTC (rev 253489)
@@ -1,3 +1,15 @@
+2019-12-13 John Wilander <[email protected]>
+
+ IsLoggedIn: Abstract data type for IsLoggedIn state
+ https://bugs.webkit.org/show_bug.cgi?id=205041
+ <rdar://problem/56723904>
+
+ Reviewed by Chris Dumez.
+
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+ * TestWebKitAPI/Tests/WebCore/LoggedInStatus.cpp: Added.
+ (TestWebKitAPI::TEST):
+
2019-12-13 Wenson Hsieh <[email protected]>
[Clipboard API] Sanitize HTML and image data written using clipboard.write
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (253488 => 253489)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2019-12-13 19:07:51 UTC (rev 253488)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2019-12-13 19:18:37 UTC (rev 253489)
@@ -441,6 +441,7 @@
6B306106218A372900F5A802 /* ClosingWebView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B306105218A372900F5A802 /* ClosingWebView.mm */; };
6B4E861C2220A5520022F389 /* RegistrableDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B4E861B2220A5520022F389 /* RegistrableDomain.cpp */; };
6B9ABE122086952F00D75DE6 /* HTTPParsers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B9ABE112086952F00D75DE6 /* HTTPParsers.cpp */; };
+ 6BF4A683239ED4CD00E2F45B /* LoggedInStatus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6BF4A682239ED4CD00E2F45B /* LoggedInStatus.cpp */; };
6BFD294C1D5E6C1D008EC968 /* HashCountedSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A38D7E51C752D5F004F157D /* HashCountedSet.cpp */; };
725C3EF322058A5B007C36FC /* AdditionalSupportedImageTypes.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 725C3EF2220584BA007C36FC /* AdditionalSupportedImageTypes.html */; };
7283A9D022FA754900B21C7D /* img-with-rotated-image.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 7283A9CE22FA6BBE00B21C7D /* img-with-rotated-image.html */; };
@@ -2008,6 +2009,7 @@
6B306105218A372900F5A802 /* ClosingWebView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ClosingWebView.mm; sourceTree = "<group>"; };
6B4E861B2220A5520022F389 /* RegistrableDomain.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = RegistrableDomain.cpp; sourceTree = "<group>"; };
6B9ABE112086952F00D75DE6 /* HTTPParsers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HTTPParsers.cpp; sourceTree = "<group>"; };
+ 6BF4A682239ED4CD00E2F45B /* LoggedInStatus.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LoggedInStatus.cpp; sourceTree = "<group>"; };
725C3EF2220584BA007C36FC /* AdditionalSupportedImageTypes.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = AdditionalSupportedImageTypes.html; sourceTree = "<group>"; };
7283A9CE22FA6BBE00B21C7D /* img-with-rotated-image.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "img-with-rotated-image.html"; sourceTree = "<group>"; };
7283A9D122FB1D9700B21C7D /* exif-orientation-8-llo.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "exif-orientation-8-llo.jpg"; sourceTree = "<group>"; };
@@ -3114,6 +3116,7 @@
7A909A751D877475007E10F8 /* IntSize.cpp */,
CD5FF4962162E27E004BD86F /* ISOBox.cpp */,
14464012167A8305000BD218 /* LayoutUnit.cpp */,
+ 6BF4A682239ED4CD00E2F45B /* LoggedInStatus.cpp */,
076E507E1F45031E006E9F5A /* Logging.cpp */,
CE1866471F72E8F100A0CAB6 /* MarkedText.cpp */,
A5B149DD1F5A19DC00C6DAFF /* MIMETypeRegistry.cpp */,
@@ -4731,6 +4734,7 @@
46C519DA1D355AB200DAA51A /* LocalStorageNullEntries.mm in Sources */,
8C10AF99206467A90018FD90 /* LocalStoragePersistence.mm in Sources */,
7A6A2C701DCCFA8C00C0D085 /* LocalStorageQuirkTest.mm in Sources */,
+ 6BF4A683239ED4CD00E2F45B /* LoggedInStatus.cpp in Sources */,
076E507F1F4513D6006E9F5A /* Logging.cpp in Sources */,
CE1866491F72E8F100A0CAB6 /* MarkedText.cpp in Sources */,
07CC7DFE2266330900E39181 /* MediaBufferingPolicy.mm in Sources */,
Added: trunk/Tools/TestWebKitAPI/Tests/WebCore/LoggedInStatus.cpp (0 => 253489)
--- trunk/Tools/TestWebKitAPI/Tests/WebCore/LoggedInStatus.cpp (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/LoggedInStatus.cpp 2019-12-13 19:18:37 UTC (rev 253489)
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <WebCore/LoggedInStatus.h>
+#include <WebCore/RegistrableDomain.h>
+#include <wtf/Seconds.h>
+#include <wtf/text/StringBuilder.h>
+
+using namespace WebCore;
+
+namespace TestWebKitAPI {
+
+// Positive test cases.
+
+TEST(LoggedInStatus, DefaultExpiryWebAuthn)
+{
+ RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
+
+ auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::WebAuthn);
+ auto& loggedIn = loggedInResult.releaseReturnValue().get();
+ auto now = WallTime::now();
+ ASSERT_EQ(loggedIn.registrableDomain().string(), "example.com"_s);
+ ASSERT_EQ(loggedIn.username(), "admin"_s);
+ ASSERT_EQ(loggedIn.credentialTokenType(), LoggedInStatus::CredentialTokenType::HTTPStateToken);
+ ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::WebAuthn);
+ ASSERT_TRUE(loggedIn.loggedInTime() < now + 60_s);
+ ASSERT_TRUE(loggedIn.loggedInTime() > now - 60_s);
+ ASSERT_TRUE(loggedIn.expiry() < now + LoggedInStatus::TimeToLiveLong + 60_s);
+ ASSERT_TRUE(loggedIn.expiry() > now + LoggedInStatus::TimeToLiveLong - 60_s);
+ ASSERT_FALSE(loggedIn.hasExpired());
+}
+
+TEST(LoggedInStatus, DefaultExpiryPasswordManager)
+{
+ RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
+
+ auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::PasswordManager);
+ auto& loggedIn = loggedInResult.releaseReturnValue().get();
+ auto now = WallTime::now();
+ ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::PasswordManager);
+ ASSERT_TRUE(loggedIn.expiry() < now + LoggedInStatus::TimeToLiveLong + 60_s);
+ ASSERT_TRUE(loggedIn.expiry() > now + LoggedInStatus::TimeToLiveLong - 60_s);
+ ASSERT_FALSE(loggedIn.hasExpired());
+}
+
+TEST(LoggedInStatus, DefaultExpiryUnmanaged)
+{
+ RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
+
+ auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::Unmanaged);
+ auto& loggedIn = loggedInResult.releaseReturnValue().get();
+ auto now = WallTime::now();
+ ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::Unmanaged);
+ ASSERT_TRUE(loggedIn.expiry() < now + LoggedInStatus::TimeToLiveShort + 60_s);
+ ASSERT_TRUE(loggedIn.expiry() > now + LoggedInStatus::TimeToLiveShort - 60_s);
+ ASSERT_FALSE(loggedIn.hasExpired());
+}
+
+TEST(LoggedInStatus, CustomExpiryBelowLong)
+{
+ RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
+
+ auto customExpiry = LoggedInStatus::TimeToLiveLong - 48_h;
+ auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::WebAuthn, customExpiry);
+ auto& loggedIn = loggedInResult.releaseReturnValue().get();
+ auto now = WallTime::now();
+ ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::WebAuthn);
+ ASSERT_TRUE(loggedIn.expiry() < now + customExpiry + 60_s);
+ ASSERT_TRUE(loggedIn.expiry() > now + customExpiry - 60_s);
+ ASSERT_FALSE(loggedIn.hasExpired());
+}
+
+TEST(LoggedInStatus, CustomExpiryBelowShort)
+{
+ RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
+
+ auto customExpiry = LoggedInStatus::TimeToLiveShort - 48_h;
+ auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::PasswordManager, customExpiry);
+ auto& loggedIn = loggedInResult.releaseReturnValue().get();
+ auto now = WallTime::now();
+ ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::PasswordManager);
+ ASSERT_TRUE(loggedIn.expiry() < now + customExpiry + 60_s);
+ ASSERT_TRUE(loggedIn.expiry() > now + customExpiry - 60_s);
+ ASSERT_FALSE(loggedIn.hasExpired());
+}
+
+TEST(LoggedInStatus, RenewedExpiry)
+{
+ RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
+
+ auto customExpiry = LoggedInStatus::TimeToLiveShort - 48_h;
+ auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::WebAuthn, customExpiry);
+ auto& loggedIn = loggedInResult.releaseReturnValue().get();
+ auto now = WallTime::now();
+ ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::WebAuthn);
+ ASSERT_TRUE(loggedIn.expiry() < now + customExpiry + 60_s);
+ ASSERT_TRUE(loggedIn.expiry() > now + customExpiry - 60_s);
+ ASSERT_FALSE(loggedIn.hasExpired());
+ auto newCustomExpiry = 24_h;
+ loggedIn.setTimeToLive(newCustomExpiry);
+ ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::WebAuthn);
+ ASSERT_TRUE(loggedIn.expiry() < now + newCustomExpiry + 60_s);
+ ASSERT_TRUE(loggedIn.expiry() > now + newCustomExpiry - 60_s);
+ ASSERT_FALSE(loggedIn.hasExpired());
+}
+
+// Negative test cases.
+
+TEST(LoggedInStatus, InvalidUsernames)
+{
+ RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
+
+ // Whitespace is not allowed.
+ auto loggedInResult = LoggedInStatus::create(loginDomain, "I use spaces"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::WebAuthn);
+ ASSERT_TRUE(loggedInResult.hasException());
+
+ // Newlines are not allowed.
+ loggedInResult = LoggedInStatus::create(loginDomain, "Enter\nreturn"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::WebAuthn);
+ ASSERT_TRUE(loggedInResult.hasException());
+
+ // There is a max length to usernames.
+ StringBuilder builder;
+ for (unsigned i = 0; i <= LoggedInStatus::UsernameMaxLength; ++i)
+ builder.append('a');
+ loggedInResult = LoggedInStatus::create(loginDomain, builder.toString(), LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::WebAuthn);
+ ASSERT_TRUE(loggedInResult.hasException());
+}
+
+TEST(LoggedInStatus, ClampedExpiryLong)
+{
+ RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
+
+ // Too long expiries for managed auth types should be clamped to LoggedInStatus::TimeToLiveLong.
+ auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::WebAuthn, LoggedInStatus::TimeToLiveLong + 24_h);
+ auto& loggedIn = loggedInResult.releaseReturnValue().get();
+ auto now = WallTime::now();
+ ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::WebAuthn);
+ ASSERT_TRUE(loggedIn.expiry() < now + LoggedInStatus::TimeToLiveLong + 60_s);
+ ASSERT_TRUE(loggedIn.expiry() > now + LoggedInStatus::TimeToLiveLong - 60_s);
+ ASSERT_FALSE(loggedIn.hasExpired());
+
+ // Renewed, too long expiries for managed auth types should also be clamped to LoggedInStatus::TimeToLiveLong.
+ auto newCustomExpiry = LoggedInStatus::TimeToLiveLong + 24_h;
+ loggedIn.setTimeToLive(newCustomExpiry);
+ ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::WebAuthn);
+ ASSERT_TRUE(loggedIn.expiry() < now + LoggedInStatus::TimeToLiveLong + 60_s);
+ ASSERT_TRUE(loggedIn.expiry() > now + LoggedInStatus::TimeToLiveLong - 60_s);
+ ASSERT_FALSE(loggedIn.hasExpired());
+}
+
+TEST(LoggedInStatus, ClampedExpiryShort)
+{
+ RegistrableDomain loginDomain { URL { { }, "https://login.example.com"_s } };
+
+ // Too long expiries for unmanaged auth types should be clamped to LoggedInStatus::TimeToLiveShort.
+ auto loggedInResult = LoggedInStatus::create(loginDomain, "admin"_s, LoggedInStatus::CredentialTokenType::HTTPStateToken, LoggedInStatus::AuthenticationType::Unmanaged, LoggedInStatus::TimeToLiveShort + 24_h);
+ auto& loggedIn = loggedInResult.releaseReturnValue().get();
+ auto now = WallTime::now();
+ ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::Unmanaged);
+ ASSERT_TRUE(loggedIn.expiry() < now + LoggedInStatus::TimeToLiveShort + 60_s);
+ ASSERT_TRUE(loggedIn.expiry() > now + LoggedInStatus::TimeToLiveShort - 60_s);
+ ASSERT_FALSE(loggedIn.hasExpired());
+
+ // Renewed, too long expiries for unmanaged auth types should also be clamped to LoggedInStatus::TimeToLiveShort.
+ auto newCustomExpiry = LoggedInStatus::TimeToLiveShort + 24_h;
+ loggedIn.setTimeToLive(newCustomExpiry);
+ ASSERT_EQ(loggedIn.authenticationType(), LoggedInStatus::AuthenticationType::Unmanaged);
+ ASSERT_TRUE(loggedIn.expiry() < now + LoggedInStatus::TimeToLiveShort + 60_s);
+ ASSERT_TRUE(loggedIn.expiry() > now + LoggedInStatus::TimeToLiveShort - 60_s);
+ ASSERT_FALSE(loggedIn.hasExpired());
+}
+
+} // namespace TestWebKitAPI