Simplify Secrets module — get rid of protocol. Just reference SecretKey properties as static.

This commit is contained in:
Brent Simmons 2025-01-06 21:02:03 -08:00
parent 1eacebe546
commit 45cd018020
19 changed files with 64 additions and 95 deletions

2
.gitignore vendored
View File

@ -74,4 +74,6 @@ fastlane/test_output
/Frameworks/Secrets/Secrets.swift
Secrets/Sources/Secrets/Secrets.swift
*.py[cod]
/Secrets/Sources/Secrets/SecretKey.swift
/Modules/Secrets/Sources/Secrets/SecretKey.swift

View File

@ -15,10 +15,10 @@ extension OAuthAuthorizationClient {
/// Models private NetNewsWire client secrets.
/// These placeholders are substituted at build time using a Run Script phase with build settings.
/// https://developer.feedly.com/v3/auth/#authenticating-a-user-and-obtaining-an-auth-code
return OAuthAuthorizationClient(id: SecretsManager.provider.feedlyClientId,
return OAuthAuthorizationClient(id: SecretKey.feedlyClientID,
redirectUri: "netnewswire://auth/feedly",
state: nil,
secret: SecretsManager.provider.feedlyClientSecret)
secret: SecretKey.feedlyClientSecret)
}
static var feedlySandboxClient: OAuthAuthorizationClient {

View File

@ -56,7 +56,7 @@ final class NewsBlurAPICaller: NSObject {
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: url)
for cookie in cookies where cookie.name == Self.SessionIdCookie {
let credentials = Credentials(type: .newsBlurSessionId, username: username, secret: cookie.value)
let credentials = Credentials(type: .newsBlurSessionID, username: username, secret: cookie.value)
completion(.success(credentials))
return
}

View File

@ -609,7 +609,7 @@ final class NewsBlurAccountDelegate: AccountDelegate {
}
func accountDidInitialize(_ account: Account) {
credentials = try? account.retrieveCredentials(type: .newsBlurSessionId)
credentials = try? account.retrieveCredentials(type: .newsBlurSessionID)
}
func accountWillBeDeleted(_ account: Account) {

View File

@ -693,8 +693,8 @@ private extension ReaderAPICaller {
func addVariantHeaders(_ request: inout URLRequest) {
if variant == .inoreader {
request.addValue(SecretsManager.provider.inoreaderAppId, forHTTPHeaderField: "AppId")
request.addValue(SecretsManager.provider.inoreaderAppKey, forHTTPHeaderField: "AppKey")
request.addValue(SecretKey.inoreaderAppID, forHTTPHeaderField: "AppId")
request.addValue(SecretKey.inoreaderAppKey, forHTTPHeaderField: "AppKey")
}
}

View File

@ -35,7 +35,7 @@ public extension URLRequest {
URLQueryItem(name: "password", value: credentials.secret),
]
httpBody = postData.enhancedPercentEncodedQuery?.data(using: .utf8)
case .newsBlurSessionId:
case .newsBlurSessionID:
setValue("\(NewsBlurAPICaller.SessionIdCookie)=\(credentials.secret)", forHTTPHeaderField: "Cookie")
httpShouldHandleCookies = true
case .readerBasic:

View File

@ -1,18 +0,0 @@
//
// FeedlyTestSecrets.swift
//
//
// Created by Maurice Parker on 8/4/20.
//
import Foundation
import Secrets
struct FeedlyTestSecrets: SecretsProvider {
var mercuryClientId = ""
var mercuryClientSecret = ""
var feedlyClientId = ""
var feedlyClientSecret = ""
var inoreaderAppId = ""
var inoreaderAppKey = ""
}

View File

@ -19,10 +19,6 @@ class FeedlyTestSupport {
var refreshToken = Credentials(type: .oauthRefreshToken, username: "Test", secret: "t3st-refresh-tok3n")
var transport = TestTransport()
init() {
SecretsManager.provider = FeedlyTestSecrets()
}
func makeMockNetworkStack() -> (TestTransport, FeedlyAPICaller) {
let caller = FeedlyAPICaller(transport: transport, api: .sandbox)
caller.credentials = accessToken

View File

@ -117,7 +117,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
crashReporter.enable()
#endif
SecretsManager.provider = Secrets()
AccountManager.shared = AccountManager(accountsFolder: Platform.dataSubfolder(forApplication: nil, folderName: "Accounts")!)
ArticleThemesManager.shared = ArticleThemesManager(folderPath: Platform.dataSubfolder(forApplication: nil, folderName: "Themes")!)

View File

@ -97,7 +97,7 @@ class AccountsNewsBlurWindowController: NSWindowController {
do {
try self.account?.removeCredentials(type: .newsBlurBasic)
try self.account?.removeCredentials(type: .newsBlurSessionId)
try self.account?.removeCredentials(type: .newsBlurSessionID)
try self.account?.storeCredentials(credentials)
try self.account?.storeCredentials(validatedCredentials)

View File

@ -396,7 +396,6 @@
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Resources/NewsFax.nnwtheme,
Secrets.swift.gyb,
ShareExtension/SafariExt.js,
ShareExtension/ShareDefaultContainer.swift,
Widget/WidgetData.swift,
@ -413,7 +412,6 @@
ExtensionPoints/SendToMarsEditCommand.swift,
ExtensionPoints/SendToMicroBlogCommand.swift,
"Extensions/NSView-Extensions.swift",
Secrets.swift.gyb,
ShareExtension/SafariExt.js,
ShareExtension/ShareDefaultContainer.swift,
SmartFeeds/SmartFeedPasteboardWriter.swift,

24
Package.swift Normal file
View File

@ -0,0 +1,24 @@
// swift-tools-version: 5.10
import PackageDescription
let package = Package(
name: "Secrets",
platforms: [.macOS(.v14), .iOS(.v17)],
products: [
.library(
name: "Secrets",
targets: ["Secrets"]
)
],
dependencies: [],
targets: [
.target(
name: "Secrets",
dependencies: [],
exclude: ["SecretKey.swift.gyb"],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency")
]
)
]
)

View File

@ -1,4 +1,4 @@
// swift-tools-version:5.10
// swift-tools-version:6.0
import PackageDescription
@ -17,7 +17,7 @@ let package = Package(
.target(
name: "Secrets",
dependencies: [],
swiftSettings: [.unsafeFlags(["-warnings-as-errors"])]
exclude: ["SecretKey.swift.gyb"]
)
]
)

View File

@ -8,15 +8,15 @@
import Foundation
public enum CredentialsError: Error {
public enum CredentialsError: Error, Sendable {
case incompleteCredentials
case unhandledError(status: OSStatus)
}
public enum CredentialsType: String {
public enum CredentialsType: String, Sendable {
case basic = "password"
case newsBlurBasic = "newsBlurBasic"
case newsBlurSessionId = "newsBlurSessionId"
case newsBlurSessionID = "newsBlurSessionId"
case readerBasic = "readerBasic"
case readerAPIKey = "readerAPIKey"
case oauthAccessToken = "oauthAccessToken"
@ -24,7 +24,7 @@ public enum CredentialsType: String {
case oauthRefreshToken = "oauthRefreshToken"
}
public struct Credentials: Equatable {
public struct Credentials: Equatable, Sendable {
public let type: CredentialsType
public let username: String
public let secret: String

View File

@ -10,7 +10,7 @@ import Foundation
public struct CredentialsManager {
private static var keychainGroup: String? = {
private static let keychainGroup: String? = {
guard let appGroup = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as? String else {
return nil
}

View File

@ -1,4 +1,4 @@
// Generated by Secrets.swift.gyb
// Generated by SecretKey.swift.gyb
%{
import os
@ -13,40 +13,37 @@ def encode(string, salt):
def snake_to_camel(snake_str):
components = snake_str.split('_')
return components[0].lower() + ''.join(x.title() for x in components[1:])
components = [components[0].lower()] + [x.title() if x != 'ID' else x for x in components[1:]]
camel_case_str = ''.join(components)
return camel_case_str
salt = [byte for byte in os.urandom(64)]
}%
import Secrets
import Foundation
public struct Secrets: SecretsProvider {
public struct SecretKey {
% for secret in secrets:
public var ${snake_to_camel(secret)}: String {
public static let ${snake_to_camel(secret)}: String = {
let encoded: [UInt8] = [
% for chunk in chunks(encode(os.environ.get(secret) or "", salt), 8):
${"".join(["0x%02x, " % byte for byte in chunk])}
% end
]
return decode(encoded, salt: salt)
}
return decode(encoded)
}()
% end
%{
# custom example: static let myVariable = "${os.environ.get('MY_CUSTOM_VARIABLE')}"
}%
private let salt: [UInt8] = [
% for chunk in chunks(salt, 8):
${"".join(["0x%02x, " % byte for byte in chunk])}
% end
]
private func decode(_ encoded: [UInt8], salt: [UInt8]) -> String {
String(decoding: encoded.enumerated().map { (offset, element) in
element ^ salt[offset % salt.count]
}, as: UTF8.self)
}
}
private let salt: [UInt8] = [
% for chunk in chunks(salt, 8):
${"".join(["0x%02x, " % byte for byte in chunk])}
% end
]
private func decode(_ encoded: [UInt8]) -> String {
String(decoding: encoded.enumerated().map { (offset, element) in
element ^ salt[offset % salt.count]
}, as: UTF8.self)
}

View File

@ -1,12 +0,0 @@
//
// SecretsManager.swift
//
//
// Created by Maurice Parker on 7/30/20.
//
import Foundation
public class SecretsManager {
public static var provider: SecretsProvider!
}

View File

@ -1,17 +0,0 @@
//
// SecretsProvider.swift
//
//
// Created by Maurice Parker on 7/30/20.
//
import Foundation
public protocol SecretsProvider {
var mercuryClientId: String { get }
var mercuryClientSecret: String { get }
var feedlyClientId: String { get }
var feedlyClientSecret: String { get }
var inoreaderAppId: String { get }
var inoreaderAppKey: String { get }
}

View File

@ -38,8 +38,8 @@ class ArticleExtractor {
self.articleLink = articleLink
let clientURL = "https://extract.feedbin.com/parser"
let username = SecretsManager.provider.mercuryClientId
let signature = articleLink.hmacUsingSHA1(key: SecretsManager.provider.mercuryClientSecret)
let username = SecretKey.mercuryClientID
let signature = articleLink.hmacUsingSHA1(key: SecretKey.mercuryClientSecret)
if let base64URL = articleLink.data(using: .utf8)?.base64EncodedString() {
let fullURL = "\(clientURL)/\(username)/\(signature)?base64_url=\(base64URL)"