@@ -28,8 +28,8 @@ public enum AuthorizationProviderError: Error {
28
28
case other( String )
29
29
}
30
30
31
- extension AuthorizationProvider {
32
- public func httpAuthorizationHeader( for url: Foundation . URL ) -> String ? {
31
+ public extension AuthorizationProvider {
32
+ func httpAuthorizationHeader( for url: Foundation . URL ) -> String ? {
33
33
guard let ( user, password) = self . authentication ( for: url) else {
34
34
return nil
35
35
}
@@ -55,9 +55,9 @@ extension Foundation.URL {
55
55
public struct NetrcAuthorizationProvider : AuthorizationProvider {
56
56
let path : AbsolutePath
57
57
private let fileSystem : FileSystem
58
-
58
+
59
59
private var underlying : TSCUtility . Netrc ?
60
-
60
+
61
61
var machines : [ TSCUtility . Netrc . Machine ] {
62
62
self . underlying? . machines ?? [ ]
63
63
}
@@ -67,17 +67,17 @@ public struct NetrcAuthorizationProvider: AuthorizationProvider {
67
67
self . fileSystem = fileSystem
68
68
self . underlying = try Self . load ( from: path)
69
69
}
70
-
70
+
71
71
public mutating func addOrUpdate( for url: Foundation . URL , user: String , password: String , callback: @escaping ( Result < Void , Error > ) -> Void ) {
72
72
guard let machine = url. authenticationID else {
73
73
return callback ( . failure( AuthorizationProviderError . invalidURLHost) )
74
74
}
75
-
75
+
76
76
// Same entry already exists, no need to add or update
77
77
guard self . machines. first ( where: { $0. name. lowercased ( ) == machine && $0. login == user && $0. password == password } ) == nil else {
78
78
return
79
79
}
80
-
80
+
81
81
do {
82
82
// Append to end of file
83
83
try self . fileSystem. withLock ( on: self . path, type: . exclusive) {
@@ -92,13 +92,13 @@ public struct NetrcAuthorizationProvider: AuthorizationProvider {
92
92
stream. write ( " \n " )
93
93
}
94
94
}
95
-
95
+
96
96
// At this point the netrc file should exist and non-empty
97
97
guard let netrc = try Self . load ( from: self . path) else {
98
98
throw AuthorizationProviderError . other ( " Failed to update netrc file at \( self . path) " )
99
99
}
100
100
self . underlying = netrc
101
-
101
+
102
102
callback ( . success( ( ) ) )
103
103
} catch {
104
104
callback ( . failure( AuthorizationProviderError . other ( " Failed to update netrc file at \( self . path) : \( error) " ) ) )
@@ -139,16 +139,16 @@ public struct NetrcAuthorizationProvider: AuthorizationProvider {
139
139
#if canImport(Security)
140
140
public struct KeychainAuthorizationProvider : AuthorizationProvider {
141
141
public init ( ) { }
142
-
142
+
143
143
public func addOrUpdate( for url: Foundation . URL , user: String , password: String , callback: @escaping ( Result < Void , Error > ) -> Void ) {
144
144
guard let server = url. authenticationID else {
145
145
return callback ( . failure( AuthorizationProviderError . invalidURLHost) )
146
146
}
147
147
guard let passwordData = password. data ( using: . utf8) else {
148
148
return callback ( . failure( AuthorizationProviderError . cannotEncodePassword) )
149
149
}
150
- let `protocol` = self . ` protocol` ( for: url)
151
-
150
+ let `protocol` = self . protocol ( for: url)
151
+
152
152
do {
153
153
if !( try self . update ( server: server, protocol: `protocol`, account: user, password: passwordData) ) {
154
154
try self . create ( server: server, protocol: `protocol`, account: user, password: passwordData)
@@ -158,17 +158,18 @@ public struct KeychainAuthorizationProvider: AuthorizationProvider {
158
158
callback ( . failure( error) )
159
159
}
160
160
}
161
-
161
+
162
162
public func authentication( for url: Foundation . URL ) -> ( user: String , password: String ) ? {
163
163
guard let server = url. authenticationID else {
164
164
return nil
165
165
}
166
-
166
+
167
167
do {
168
- guard let existingItem = try self . search ( server: server, protocol: self . ` protocol` ( for: url) ) as? [ String : Any ] ,
168
+ guard let existingItem = try self . search ( server: server, protocol: self . protocol ( for: url) ) as? [ String : Any ] ,
169
169
let passwordData = existingItem [ kSecValueData as String ] as? Data ,
170
170
let password = String ( data: passwordData, encoding: String . Encoding. utf8) ,
171
- let account = existingItem [ kSecAttrAccount as String ] as? String else {
171
+ let account = existingItem [ kSecAttrAccount as String ] as? String
172
+ else {
172
173
throw AuthorizationProviderError . other ( " Failed to extract credentials for server \( server) from keychain " )
173
174
}
174
175
return ( user: account, password: password)
@@ -184,27 +185,27 @@ public struct KeychainAuthorizationProvider: AuthorizationProvider {
184
185
return nil
185
186
}
186
187
}
187
-
188
- private func create( server: String , ` protocol` : CFString , account: String , password: Data ) throws {
188
+
189
+ private func create( server: String , protocol: CFString , account: String , password: Data ) throws {
189
190
let query : [ String : Any ] = [ kSecClass as String : kSecClassInternetPassword,
190
191
kSecAttrServer as String : server,
191
192
kSecAttrProtocol as String : `protocol`,
192
193
kSecAttrAccount as String : account,
193
194
kSecValueData as String : password]
194
-
195
+
195
196
let status = SecItemAdd ( query as CFDictionary , nil )
196
197
guard status == errSecSuccess else {
197
198
throw AuthorizationProviderError . other ( " Failed to save credentials for server \( server) to keychain: status \( status) " )
198
199
}
199
200
}
200
-
201
- private func update( server: String , ` protocol` : CFString , account: String , password: Data ) throws -> Bool {
201
+
202
+ private func update( server: String , protocol: CFString , account: String , password: Data ) throws -> Bool {
202
203
let query : [ String : Any ] = [ kSecClass as String : kSecClassInternetPassword,
203
204
kSecAttrServer as String : server,
204
205
kSecAttrProtocol as String : `protocol`]
205
206
let attributes : [ String : Any ] = [ kSecAttrAccount as String : account,
206
207
kSecValueData as String : password]
207
-
208
+
208
209
let status = SecItemUpdate ( query as CFDictionary , attributes as CFDictionary )
209
210
guard status != errSecItemNotFound else {
210
211
return false
@@ -214,15 +215,15 @@ public struct KeychainAuthorizationProvider: AuthorizationProvider {
214
215
}
215
216
return true
216
217
}
217
-
218
- private func search( server: String , ` protocol` : CFString ) throws -> CFTypeRef ? {
218
+
219
+ private func search( server: String , protocol: CFString ) throws -> CFTypeRef ? {
219
220
let query : [ String : Any ] = [ kSecClass as String : kSecClassInternetPassword,
220
221
kSecAttrServer as String : server,
221
222
kSecAttrProtocol as String : `protocol`,
222
223
kSecMatchLimit as String : kSecMatchLimitOne,
223
224
kSecReturnAttributes as String : true ,
224
225
kSecReturnData as String : true ]
225
-
226
+
226
227
var item : CFTypeRef ?
227
228
// Search keychain for server's username and password, if any.
228
229
let status = SecItemCopyMatching ( query as CFDictionary , & item)
@@ -232,10 +233,10 @@ public struct KeychainAuthorizationProvider: AuthorizationProvider {
232
233
guard status == errSecSuccess else {
233
234
throw AuthorizationProviderError . other ( " Failed to find credentials for server \( server) in keychain: status \( status) " )
234
235
}
235
-
236
+
236
237
return item
237
238
}
238
-
239
+
239
240
private func `protocol`( for url: Foundation . URL ) -> CFString {
240
241
// See https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_attribute_keys_and_values?language=swift
241
242
// for a list of possible values for the `kSecAttrProtocol` attribute.
@@ -253,15 +254,15 @@ public struct KeychainAuthorizationProvider: AuthorizationProvider {
253
254
254
255
public struct CompositeAuthorizationProvider : AuthorizationProvider {
255
256
private let providers : [ AuthorizationProvider ]
256
-
257
+
257
258
public init ( _ providers: AuthorizationProvider ... ) {
258
259
self . init ( providers)
259
260
}
260
-
261
+
261
262
public init ( _ providers: [ AuthorizationProvider ] ) {
262
263
self . providers = providers
263
264
}
264
-
265
+
265
266
public func authentication( for url: Foundation . URL ) -> ( user: String , password: String ) ? {
266
267
for provider in self . providers {
267
268
if let authentication = provider. authentication ( for: url) {
0 commit comments