Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Modules/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ let package = Package(
.target(
name: "WordPressShared",
dependencies: [
"BuildSettingsKit",
.product(name: "SwiftSoup", package: "SwiftSoup"),
.target(name: "SFHFKeychainUtils"),
.target(name: "WordPressSharedObjC")
Expand Down
1 change: 1 addition & 0 deletions Modules/Sources/BuildSettingsKit/BuildSettings+Live.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ extension BuildSettings {
pushNotificationAppID = bundle.infoValue(forKey: "WPPushNotificationAppID")
appGroupName = bundle.infoValue(forKey: "WPAppGroupName")
appKeychainAccessGroup = bundle.infoValue(forKey: "WPAppKeychainAccessGroup")
sharedKeychainAccessGroup = bundle.object(forInfoDictionaryKey: "WPSharedKeychainAccessGroup") as? String
eventNamePrefix = bundle.infoValue(forKey: "WPEventNamePrefix")
explatPlatform = bundle.infoValue(forKey: "WPExplatPlatform")
itunesAppID = bundle.infoValue(forKey: "WPItunesAppID")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ extension BuildSettings {
pushNotificationAppID: "xcpreview_push_notification_id",
appGroupName: "xcpreview_app_group_name",
appKeychainAccessGroup: "xcpreview_app_keychain_access_group",
sharedKeychainAccessGroup: "xcpreview_shared_keychain_access_group",
eventNamePrefix: "xcpreview",
explatPlatform: "xcpreview",
itunesAppID: "1234567890",
Expand Down
4 changes: 4 additions & 0 deletions Modules/Sources/BuildSettingsKit/BuildSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ public struct BuildSettings: Sendable {
public var pushNotificationAppID: String
public var appGroupName: String
public var appKeychainAccessGroup: String
/// The legacy cross-app keychain group shared by the WordPress and
/// Jetpack apps. nil where the app has no shared-group entitlement
/// (Reader): the key is simply absent from that app's Info.plist.
public var sharedKeychainAccessGroup: String?
public var eventNamePrefix: String
public var explatPlatform: String
public var itunesAppID: String
Expand Down
78 changes: 39 additions & 39 deletions Modules/Sources/ShareExtensionCore/ShareExtensionService.swift
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
import Foundation
import BuildSettingsKit
import SFHFKeychainUtils
import WordPressShared

public final class ShareExtensionService {
private let appGroupName: String
private let appKeychainAccessGroup: String
private let configuration: ShareExtensionConfiguration
private let keychain: any KeychainAccessible

public convenience init() {
let settings = BuildSettings.current
self.init(
appGroupName: settings.appGroupName,
appKeychainAccessGroup: settings.appKeychainAccessGroup,
configuration: settings.shareExtensionConfiguration
)
}

public init(
appGroupName: String,
appKeychainAccessGroup: String,
configuration: ShareExtensionConfiguration
configuration: ShareExtensionConfiguration,
keychain: any KeychainAccessible = AppKeychain()
) {
self.appGroupName = appGroupName
self.appKeychainAccessGroup = appKeychainAccessGroup
self.configuration = configuration
self.keychain = keychain
}

/// Sets the OAuth Token that should be used by the Share Extension to hit the Dotcom Backend.
Expand All @@ -32,12 +31,10 @@ public final class ShareExtensionService {
///
public func storeToken(_ oauth2Token: String) {
do {
try SFHFKeychainUtils.storeUsername(
configuration.keychainTokenKey,
andPassword: oauth2Token,
forServiceName: configuration.keychainServiceName,
accessGroup: appKeychainAccessGroup,
updateExisting: true
try keychain.setPassword(
for: configuration.keychainTokenKey,
to: oauth2Token,
serviceName: configuration.keychainServiceName
)
} catch {
print("Error while saving Share Extension OAuth bearer token: \(error)")
Expand All @@ -50,12 +47,10 @@ public final class ShareExtensionService {
///
public func storeUsername(_ username: String) {
do {
try SFHFKeychainUtils.storeUsername(
configuration.keychainUsernameKey,
andPassword: username,
forServiceName: configuration.keychainServiceName,
accessGroup: appKeychainAccessGroup,
updateExisting: true
try keychain.setPassword(
for: configuration.keychainUsernameKey,
to: username,
serviceName: configuration.keychainServiceName
)
} catch {
print("Error while saving Share Extension OAuth bearer token: \(error)")
Expand Down Expand Up @@ -121,20 +116,20 @@ public final class ShareExtensionService {
///
public func removeShareExtensionConfiguration() {
do {
try SFHFKeychainUtils.deleteItem(
forUsername: configuration.keychainTokenKey,
andServiceName: configuration.keychainServiceName,
accessGroup: appKeychainAccessGroup
try keychain.setPassword(
for: configuration.keychainTokenKey,
to: nil,
serviceName: configuration.keychainServiceName
)
} catch {
print("Error while removing Share Extension OAuth2 bearer token: \(error)")
}

do {
try SFHFKeychainUtils.deleteItem(
forUsername: configuration.keychainUsernameKey,
andServiceName: configuration.keychainServiceName,
accessGroup: appKeychainAccessGroup
try keychain.setPassword(
for: configuration.keychainUsernameKey,
to: nil,
serviceName: configuration.keychainServiceName
)
} catch {
print("Error while removing Share Extension Username: \(error)")
Expand All @@ -153,11 +148,12 @@ public final class ShareExtensionService {
/// Retrieves the WordPress.com OAuth Token, meant for Extension usage.
///
public func retrieveShareExtensionToken() -> String? {
guard let oauth2Token = try? SFHFKeychainUtils.getPasswordForUsername(
configuration.keychainTokenKey,
andServiceName: configuration.keychainServiceName,
accessGroup: appKeychainAccessGroup
) else {
guard
let oauth2Token = try? keychain.getPassword(
for: configuration.keychainTokenKey,
serviceName: configuration.keychainServiceName
)
else {
return nil
}

Expand All @@ -167,11 +163,12 @@ public final class ShareExtensionService {
/// Retrieves the WordPress.com Username, meant for Extension usage.
///
public func retrieveShareExtensionUsername() -> String? {
guard let oauth2Token = try? SFHFKeychainUtils.getPasswordForUsername(
configuration.keychainUsernameKey,
andServiceName: configuration.keychainServiceName,
accessGroup: appKeychainAccessGroup
) else {
guard
let oauth2Token = try? keychain.getPassword(
for: configuration.keychainUsernameKey,
serviceName: configuration.keychainServiceName
)
else {
return nil
}

Expand All @@ -186,7 +183,8 @@ public final class ShareExtensionService {
}

if let siteID = userDefaults.object(forKey: configuration.userDefaultsPrimarySiteID) as? Int,
let siteName = userDefaults.object(forKey: configuration.userDefaultsPrimarySiteName) as? String {
let siteName = userDefaults.object(forKey: configuration.userDefaultsPrimarySiteName) as? String
{
return (siteID, siteName)
}

Expand All @@ -202,12 +200,14 @@ public final class ShareExtensionService {
}

if let siteID = userDefaults.object(forKey: configuration.userDefaultsLastUsedSiteID) as? Int,
let siteName = userDefaults.object(forKey: configuration.userDefaultsLastUsedSiteName) as? String {
let siteName = userDefaults.object(forKey: configuration.userDefaultsLastUsedSiteName) as? String
{
return (siteID, siteName)
}

if let siteID = userDefaults.object(forKey: configuration.userDefaultsPrimarySiteID) as? Int,
let siteName = userDefaults.object(forKey: configuration.userDefaultsPrimarySiteName) as? String {
let siteName = userDefaults.object(forKey: configuration.userDefaultsPrimarySiteName) as? String
{
return (siteID, siteName)
}

Expand Down
14 changes: 7 additions & 7 deletions Modules/Sources/WordPressData/Swift/Blog+SelfHosted.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public extension Blog {
xmlrpcEndpointURL: URL,
blogID: TaggedManagedObjectID<Blog>?,
in contextManager: ContextManager,
using keychainImplementation: KeychainAccessible = KeychainUtils()
using keychainImplementation: KeychainAccessible = AppKeychain()
) async throws -> TaggedManagedObjectID<Blog> {
try await contextManager.performAndSave { context in
let blog =
Expand Down Expand Up @@ -69,13 +69,13 @@ public extension Blog {

/// Retrieve Application Tokens
///
func getApplicationToken(using keychainImplementation: KeychainAccessible = KeychainUtils()) throws -> String {
func getApplicationToken(using keychainImplementation: KeychainAccessible = AppKeychain()) throws -> String {
try keychainImplementation.getPassword(for: self.getUsername(), serviceName: self.getUrlString())
}

/// Delete Application Token
///
func deleteApplicationToken(using keychainImplementation: KeychainAccessible = KeychainUtils()) throws {
func deleteApplicationToken(using keychainImplementation: KeychainAccessible = AppKeychain()) throws {
try? keychainImplementation.setPassword(for: self.getUsername(), to: nil, serviceName: self.getUrlString())
}

Expand All @@ -89,7 +89,7 @@ public extension Blog {
///
func setApplicationToken(
_ newValue: String,
using keychainImplementation: KeychainAccessible = KeychainUtils()
using keychainImplementation: KeychainAccessible = AppKeychain()
) throws {
try keychainImplementation.setPassword(for: self.getUsername(), to: newValue, serviceName: self.getUrlString())
}
Expand All @@ -104,15 +104,15 @@ public extension Blog {
}

/// A null-safe replacement for `Blog.password(get)`
func getPassword(using keychainImplementation: KeychainAccessible = KeychainUtils()) throws -> String {
func getPassword(using keychainImplementation: KeychainAccessible = AppKeychain()) throws -> String {
try keychainImplementation.getPassword(
for: self.getUsername(),
serviceName: self.getXMLRPCEndpoint().absoluteString
)
}

/// A null-safe replacement for `Blog.password(set)`
func setPassword(to newValue: String, using keychainImplementation: KeychainAccessible = KeychainUtils()) throws {
func setPassword(to newValue: String, using keychainImplementation: KeychainAccessible = AppKeychain()) throws {
try keychainImplementation.setPassword(
for: self.getUsername(),
to: newValue,
Expand Down Expand Up @@ -272,7 +272,7 @@ extension WordPressSite {
///
/// For self-hosted sites, application password credentials are required.
/// Sites without them cannot be represented as a `WordPressSite`.
public init(blog: Blog, keychain: KeychainAccessible = KeychainUtils()) throws {
public init(blog: Blog, keychain: KeychainAccessible = AppKeychain()) throws {
let siteURL = try blog.getUrl()
self.blogId = TaggedManagedObjectID(blog)
self.siteURL = siteURL
Expand Down
30 changes: 19 additions & 11 deletions Modules/Sources/WordPressData/Swift/Blog+Swift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ extension Blog {
/// Injectable keychain for testability.
var keychain: any KeychainAccessible {
get {
objc_getAssociatedObject(self, &blogKeychainKey) as? (any KeychainAccessible) ?? KeychainUtils()
objc_getAssociatedObject(self, &blogKeychainKey) as? (any KeychainAccessible) ?? AppKeychain()
}
set {
objc_setAssociatedObject(self, &blogKeychainKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
Expand All @@ -66,7 +66,8 @@ extension Blog {
@objc public var password: String? {
get {
guard let username, !username.isEmpty,
let xmlrpc, !xmlrpc.isEmpty else {
let xmlrpc, !xmlrpc.isEmpty
else {
return nil
}
if let password = try? keychain.getPassword(for: username, serviceName: xmlrpc) {
Expand Down Expand Up @@ -181,11 +182,13 @@ extension Blog {
@objc public var timeZone: TimeZone? {
let oneHourInSeconds: Double = 3600
if let name = getOptionString(name: "timezone"), !name.isEmpty,
let timeZone = TimeZone(identifier: name) {
let timeZone = TimeZone(identifier: name)
{
return timeZone
}
if let gmtOffset = getOptionValue("gmt_offset") as? NSNumber,
let timeZone = TimeZone(secondsFromGMT: Int(gmtOffset.doubleValue * oneHourInSeconds)) {
let timeZone = TimeZone(secondsFromGMT: Int(gmtOffset.doubleValue * oneHourInSeconds))
{
return timeZone
}
if let value = getOptionValue("time_zone") {
Expand All @@ -205,7 +208,8 @@ extension Blog {
for protectionSpace in storage.allCredentials.keys {
if protectionSpace.host == url.host
&& protectionSpace.port == (url.port ?? 80)
&& protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic {
&& protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic
{
return true
}
}
Expand All @@ -217,13 +221,15 @@ extension Blog {
@objc public var logDescription: String {
let extra: String
if let account {
extra = " wp.com account: \(account.username) blogId: \(dotComID?.intValue ?? 0) plan: \(planTitle ?? "") (\(planID?.intValue ?? 0))"
extra =
" wp.com account: \(account.username) blogId: \(dotComID?.intValue ?? 0) plan: \(planTitle ?? "") (\(planID?.intValue ?? 0))"
} else if let jetpack {
extra = " jetpack: \(jetpack)"
} else {
extra = ""
}
return "<Blog Name: \(settings?.name ?? "") URL: \(url ?? "") XML-RPC: \(xmlrpc ?? "")\(extra) ObjectID: \(objectID.uriRepresentation())>"
return
"<Blog Name: \(settings?.name ?? "") URL: \(url ?? "") XML-RPC: \(xmlrpc ?? "")\(extra) ObjectID: \(objectID.uriRepresentation())>"
}

// MARK: - Misc
Expand Down Expand Up @@ -266,9 +272,10 @@ extension Blog {

/// The blog's categories sorted alphabetically by name (case-insensitive).
@objc public var sortedCategories: [PostCategory] {
(categories ?? []).sorted {
$0.categoryName.caseInsensitiveCompare($1.categoryName) == .orderedAscending
}
(categories ?? [])
.sorted {
$0.categoryName.caseInsensitiveCompare($1.categoryName) == .orderedAscending
}
}

/// The set of allowed file types for uploads, derived from blog options.
Expand All @@ -286,7 +293,8 @@ extension Blog {
public var siteVisibility: SiteVisibility {
get {
guard let rawValue = settings?.privacy?.intValue,
let visibility = SiteVisibility(rawValue: rawValue) else {
let visibility = SiteVisibility(rawValue: rawValue)
else {
return .unknown
}
return visibility
Expand Down
Loading