Simplify Secrets module — get rid of protocol. Just reference SecretKey properties as static.
This commit is contained in:
parent
1eacebe546
commit
45cd018020
2
.gitignore
vendored
2
.gitignore
vendored
@ -74,4 +74,6 @@ fastlane/test_output
|
|||||||
/Frameworks/Secrets/Secrets.swift
|
/Frameworks/Secrets/Secrets.swift
|
||||||
Secrets/Sources/Secrets/Secrets.swift
|
Secrets/Sources/Secrets/Secrets.swift
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
/Secrets/Sources/Secrets/SecretKey.swift
|
||||||
/Modules/Secrets/Sources/Secrets/SecretKey.swift
|
/Modules/Secrets/Sources/Secrets/SecretKey.swift
|
||||||
|
|
||||||
|
@ -15,10 +15,10 @@ extension OAuthAuthorizationClient {
|
|||||||
/// Models private NetNewsWire client secrets.
|
/// Models private NetNewsWire client secrets.
|
||||||
/// These placeholders are substituted at build time using a Run Script phase with build settings.
|
/// 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
|
/// 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",
|
redirectUri: "netnewswire://auth/feedly",
|
||||||
state: nil,
|
state: nil,
|
||||||
secret: SecretsManager.provider.feedlyClientSecret)
|
secret: SecretKey.feedlyClientSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
static var feedlySandboxClient: OAuthAuthorizationClient {
|
static var feedlySandboxClient: OAuthAuthorizationClient {
|
||||||
|
@ -56,7 +56,7 @@ final class NewsBlurAPICaller: NSObject {
|
|||||||
|
|
||||||
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: url)
|
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: url)
|
||||||
for cookie in cookies where cookie.name == Self.SessionIdCookie {
|
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))
|
completion(.success(credentials))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -609,7 +609,7 @@ final class NewsBlurAccountDelegate: AccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func accountDidInitialize(_ account: Account) {
|
func accountDidInitialize(_ account: Account) {
|
||||||
credentials = try? account.retrieveCredentials(type: .newsBlurSessionId)
|
credentials = try? account.retrieveCredentials(type: .newsBlurSessionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func accountWillBeDeleted(_ account: Account) {
|
func accountWillBeDeleted(_ account: Account) {
|
||||||
|
@ -693,8 +693,8 @@ private extension ReaderAPICaller {
|
|||||||
|
|
||||||
func addVariantHeaders(_ request: inout URLRequest) {
|
func addVariantHeaders(_ request: inout URLRequest) {
|
||||||
if variant == .inoreader {
|
if variant == .inoreader {
|
||||||
request.addValue(SecretsManager.provider.inoreaderAppId, forHTTPHeaderField: "AppId")
|
request.addValue(SecretKey.inoreaderAppID, forHTTPHeaderField: "AppId")
|
||||||
request.addValue(SecretsManager.provider.inoreaderAppKey, forHTTPHeaderField: "AppKey")
|
request.addValue(SecretKey.inoreaderAppKey, forHTTPHeaderField: "AppKey")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ public extension URLRequest {
|
|||||||
URLQueryItem(name: "password", value: credentials.secret),
|
URLQueryItem(name: "password", value: credentials.secret),
|
||||||
]
|
]
|
||||||
httpBody = postData.enhancedPercentEncodedQuery?.data(using: .utf8)
|
httpBody = postData.enhancedPercentEncodedQuery?.data(using: .utf8)
|
||||||
case .newsBlurSessionId:
|
case .newsBlurSessionID:
|
||||||
setValue("\(NewsBlurAPICaller.SessionIdCookie)=\(credentials.secret)", forHTTPHeaderField: "Cookie")
|
setValue("\(NewsBlurAPICaller.SessionIdCookie)=\(credentials.secret)", forHTTPHeaderField: "Cookie")
|
||||||
httpShouldHandleCookies = true
|
httpShouldHandleCookies = true
|
||||||
case .readerBasic:
|
case .readerBasic:
|
||||||
|
@ -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 = ""
|
|
||||||
}
|
|
@ -19,10 +19,6 @@ class FeedlyTestSupport {
|
|||||||
var refreshToken = Credentials(type: .oauthRefreshToken, username: "Test", secret: "t3st-refresh-tok3n")
|
var refreshToken = Credentials(type: .oauthRefreshToken, username: "Test", secret: "t3st-refresh-tok3n")
|
||||||
var transport = TestTransport()
|
var transport = TestTransport()
|
||||||
|
|
||||||
init() {
|
|
||||||
SecretsManager.provider = FeedlyTestSecrets()
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeMockNetworkStack() -> (TestTransport, FeedlyAPICaller) {
|
func makeMockNetworkStack() -> (TestTransport, FeedlyAPICaller) {
|
||||||
let caller = FeedlyAPICaller(transport: transport, api: .sandbox)
|
let caller = FeedlyAPICaller(transport: transport, api: .sandbox)
|
||||||
caller.credentials = accessToken
|
caller.credentials = accessToken
|
||||||
|
@ -117,7 +117,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
|||||||
crashReporter.enable()
|
crashReporter.enable()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SecretsManager.provider = Secrets()
|
|
||||||
AccountManager.shared = AccountManager(accountsFolder: Platform.dataSubfolder(forApplication: nil, folderName: "Accounts")!)
|
AccountManager.shared = AccountManager(accountsFolder: Platform.dataSubfolder(forApplication: nil, folderName: "Accounts")!)
|
||||||
ArticleThemesManager.shared = ArticleThemesManager(folderPath: Platform.dataSubfolder(forApplication: nil, folderName: "Themes")!)
|
ArticleThemesManager.shared = ArticleThemesManager(folderPath: Platform.dataSubfolder(forApplication: nil, folderName: "Themes")!)
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ class AccountsNewsBlurWindowController: NSWindowController {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
try self.account?.removeCredentials(type: .newsBlurBasic)
|
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(credentials)
|
||||||
try self.account?.storeCredentials(validatedCredentials)
|
try self.account?.storeCredentials(validatedCredentials)
|
||||||
|
|
||||||
|
@ -396,7 +396,6 @@
|
|||||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||||
membershipExceptions = (
|
membershipExceptions = (
|
||||||
Resources/NewsFax.nnwtheme,
|
Resources/NewsFax.nnwtheme,
|
||||||
Secrets.swift.gyb,
|
|
||||||
ShareExtension/SafariExt.js,
|
ShareExtension/SafariExt.js,
|
||||||
ShareExtension/ShareDefaultContainer.swift,
|
ShareExtension/ShareDefaultContainer.swift,
|
||||||
Widget/WidgetData.swift,
|
Widget/WidgetData.swift,
|
||||||
@ -413,7 +412,6 @@
|
|||||||
ExtensionPoints/SendToMarsEditCommand.swift,
|
ExtensionPoints/SendToMarsEditCommand.swift,
|
||||||
ExtensionPoints/SendToMicroBlogCommand.swift,
|
ExtensionPoints/SendToMicroBlogCommand.swift,
|
||||||
"Extensions/NSView-Extensions.swift",
|
"Extensions/NSView-Extensions.swift",
|
||||||
Secrets.swift.gyb,
|
|
||||||
ShareExtension/SafariExt.js,
|
ShareExtension/SafariExt.js,
|
||||||
ShareExtension/ShareDefaultContainer.swift,
|
ShareExtension/ShareDefaultContainer.swift,
|
||||||
SmartFeeds/SmartFeedPasteboardWriter.swift,
|
SmartFeeds/SmartFeedPasteboardWriter.swift,
|
||||||
|
24
Package.swift
Normal file
24
Package.swift
Normal 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")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
@ -1,4 +1,4 @@
|
|||||||
// swift-tools-version:5.10
|
// swift-tools-version:6.0
|
||||||
|
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ let package = Package(
|
|||||||
.target(
|
.target(
|
||||||
name: "Secrets",
|
name: "Secrets",
|
||||||
dependencies: [],
|
dependencies: [],
|
||||||
swiftSettings: [.unsafeFlags(["-warnings-as-errors"])]
|
exclude: ["SecretKey.swift.gyb"]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -8,15 +8,15 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum CredentialsError: Error {
|
public enum CredentialsError: Error, Sendable {
|
||||||
case incompleteCredentials
|
case incompleteCredentials
|
||||||
case unhandledError(status: OSStatus)
|
case unhandledError(status: OSStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum CredentialsType: String {
|
public enum CredentialsType: String, Sendable {
|
||||||
case basic = "password"
|
case basic = "password"
|
||||||
case newsBlurBasic = "newsBlurBasic"
|
case newsBlurBasic = "newsBlurBasic"
|
||||||
case newsBlurSessionId = "newsBlurSessionId"
|
case newsBlurSessionID = "newsBlurSessionId"
|
||||||
case readerBasic = "readerBasic"
|
case readerBasic = "readerBasic"
|
||||||
case readerAPIKey = "readerAPIKey"
|
case readerAPIKey = "readerAPIKey"
|
||||||
case oauthAccessToken = "oauthAccessToken"
|
case oauthAccessToken = "oauthAccessToken"
|
||||||
@ -24,7 +24,7 @@ public enum CredentialsType: String {
|
|||||||
case oauthRefreshToken = "oauthRefreshToken"
|
case oauthRefreshToken = "oauthRefreshToken"
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Credentials: Equatable {
|
public struct Credentials: Equatable, Sendable {
|
||||||
public let type: CredentialsType
|
public let type: CredentialsType
|
||||||
public let username: String
|
public let username: String
|
||||||
public let secret: String
|
public let secret: String
|
||||||
|
@ -10,7 +10,7 @@ import Foundation
|
|||||||
|
|
||||||
public struct CredentialsManager {
|
public struct CredentialsManager {
|
||||||
|
|
||||||
private static var keychainGroup: String? = {
|
private static let keychainGroup: String? = {
|
||||||
guard let appGroup = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as? String else {
|
guard let appGroup = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as? String else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Generated by Secrets.swift.gyb
|
// Generated by SecretKey.swift.gyb
|
||||||
%{
|
%{
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@ -13,40 +13,37 @@ def encode(string, salt):
|
|||||||
|
|
||||||
def snake_to_camel(snake_str):
|
def snake_to_camel(snake_str):
|
||||||
components = snake_str.split('_')
|
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)]
|
salt = [byte for byte in os.urandom(64)]
|
||||||
}%
|
}%
|
||||||
import Secrets
|
import Foundation
|
||||||
|
|
||||||
public struct Secrets: SecretsProvider {
|
public struct SecretKey {
|
||||||
% for secret in secrets:
|
% for secret in secrets:
|
||||||
|
|
||||||
public var ${snake_to_camel(secret)}: String {
|
public static let ${snake_to_camel(secret)}: String = {
|
||||||
let encoded: [UInt8] = [
|
let encoded: [UInt8] = [
|
||||||
% for chunk in chunks(encode(os.environ.get(secret) or "", salt), 8):
|
% for chunk in chunks(encode(os.environ.get(secret) or "", salt), 8):
|
||||||
${"".join(["0x%02x, " % byte for byte in chunk])}
|
${"".join(["0x%02x, " % byte for byte in chunk])}
|
||||||
% end
|
% end
|
||||||
]
|
]
|
||||||
|
|
||||||
return decode(encoded, salt: salt)
|
return decode(encoded)
|
||||||
}
|
}()
|
||||||
% end
|
% end
|
||||||
|
}
|
||||||
|
|
||||||
%{
|
private let salt: [UInt8] = [
|
||||||
# custom example: static let myVariable = "${os.environ.get('MY_CUSTOM_VARIABLE')}"
|
% for chunk in chunks(salt, 8):
|
||||||
}%
|
|
||||||
|
|
||||||
private let salt: [UInt8] = [
|
|
||||||
% for chunk in chunks(salt, 8):
|
|
||||||
${"".join(["0x%02x, " % byte for byte in chunk])}
|
${"".join(["0x%02x, " % byte for byte in chunk])}
|
||||||
% end
|
% end
|
||||||
]
|
]
|
||||||
|
|
||||||
private func decode(_ encoded: [UInt8], salt: [UInt8]) -> String {
|
private func decode(_ encoded: [UInt8]) -> String {
|
||||||
String(decoding: encoded.enumerated().map { (offset, element) in
|
String(decoding: encoded.enumerated().map { (offset, element) in
|
||||||
element ^ salt[offset % salt.count]
|
element ^ salt[offset % salt.count]
|
||||||
}, as: UTF8.self)
|
}, as: UTF8.self)
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -1,12 +0,0 @@
|
|||||||
//
|
|
||||||
// SecretsManager.swift
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Created by Maurice Parker on 7/30/20.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public class SecretsManager {
|
|
||||||
public static var provider: SecretsProvider!
|
|
||||||
}
|
|
@ -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 }
|
|
||||||
}
|
|
@ -38,8 +38,8 @@ class ArticleExtractor {
|
|||||||
self.articleLink = articleLink
|
self.articleLink = articleLink
|
||||||
|
|
||||||
let clientURL = "https://extract.feedbin.com/parser"
|
let clientURL = "https://extract.feedbin.com/parser"
|
||||||
let username = SecretsManager.provider.mercuryClientId
|
let username = SecretKey.mercuryClientID
|
||||||
let signature = articleLink.hmacUsingSHA1(key: SecretsManager.provider.mercuryClientSecret)
|
let signature = articleLink.hmacUsingSHA1(key: SecretKey.mercuryClientSecret)
|
||||||
|
|
||||||
if let base64URL = articleLink.data(using: .utf8)?.base64EncodedString() {
|
if let base64URL = articleLink.data(using: .utf8)?.base64EncodedString() {
|
||||||
let fullURL = "\(clientURL)/\(username)/\(signature)?base64_url=\(base64URL)"
|
let fullURL = "\(clientURL)/\(username)/\(signature)?base64_url=\(base64URL)"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user