@@ -18,42 +18,78 @@ import Foundation
18
18
@discussion URLCredential.Storage implements a singleton object (shared instance) which manages the shared credentials cache. Note: Whereas in Mac OS X any application can access any credential with a persistence of URLCredential.Persistence.permanent provided the user gives permission, in iPhone OS an application can access only its own credentials.
19
19
*/
20
20
open class URLCredentialStorage : NSObject {
21
-
21
+
22
+ private static var _shared = URLCredentialStorage ( )
23
+
22
24
/*!
23
25
@method sharedCredentialStorage
24
26
@abstract Get the shared singleton authentication storage
25
27
@result the shared authentication storage
26
28
*/
27
- open class var shared : URLCredentialStorage { get { NSUnimplemented ( ) } }
28
-
29
+ open class var shared : URLCredentialStorage { return _shared }
30
+
31
+ private let _lock : NSLock
32
+ private var _credentials : [ URLProtectionSpace : [ String : URLCredential ] ]
33
+ private var _defaultCredentials : [ URLProtectionSpace : URLCredential ]
34
+
35
+ public override init ( ) {
36
+ _lock = NSLock ( )
37
+ _credentials = [ : ]
38
+ _defaultCredentials = [ : ]
39
+ }
40
+
29
41
/*!
30
42
@method credentialsForProtectionSpace:
31
43
@abstract Get a dictionary mapping usernames to credentials for the specified protection space.
32
44
@param protectionSpace An URLProtectionSpace indicating the protection space for which to get credentials
33
45
@result A dictionary where the keys are usernames and the values are the corresponding URLCredentials.
34
46
*/
35
- open func credentials( for space: URLProtectionSpace ) -> [ String : URLCredential ] ? { NSUnimplemented ( ) }
36
-
47
+ open func credentials( for space: URLProtectionSpace ) -> [ String : URLCredential ] ? {
48
+ _lock. lock ( )
49
+ defer { _lock. unlock ( ) }
50
+ return _credentials [ space]
51
+ }
52
+
37
53
/*!
38
54
@method allCredentials
39
55
@abstract Get a dictionary mapping URLProtectionSpaces to dictionaries which map usernames to URLCredentials
40
56
@result an NSDictionary where the keys are URLProtectionSpaces
41
57
and the values are dictionaries, in which the keys are usernames
42
58
and the values are URLCredentials
43
59
*/
44
- open var allCredentials : [ URLProtectionSpace : [ String : URLCredential ] ] { NSUnimplemented ( ) }
45
-
60
+ open var allCredentials : [ URLProtectionSpace : [ String : URLCredential ] ] {
61
+ _lock. lock ( )
62
+ defer { _lock. unlock ( ) }
63
+ return _credentials
64
+ }
65
+
46
66
/*!
47
67
@method setCredential:forProtectionSpace:
48
68
@abstract Add a new credential to the set for the specified protection space or replace an existing one.
49
69
@param credential The credential to set.
50
- @param space The protection space for which to add it.
70
+ @param space The protection space for which to add it.
51
71
@discussion Multiple credentials may be set for a given protection space, but each must have
52
72
a distinct user. If a credential with the same user is already set for the protection space,
53
73
the new one will replace it.
54
74
*/
55
- open func set( _ credential: URLCredential , for space: URLProtectionSpace ) { NSUnimplemented ( ) }
56
-
75
+ open func set( _ credential: URLCredential , for space: URLProtectionSpace ) {
76
+ guard credential. persistence != . synchronizable else {
77
+ NSUnimplemented ( )
78
+ }
79
+
80
+ guard credential. persistence != . none else {
81
+ return
82
+ }
83
+
84
+ _lock. lock ( )
85
+ let needsNotification = _setWhileLocked ( credential, for: space)
86
+ _lock. unlock ( )
87
+
88
+ if needsNotification {
89
+ _sendNotificationWhileUnlocked ( )
90
+ }
91
+ }
92
+
57
93
/*!
58
94
@method removeCredential:forProtectionSpace:
59
95
@abstract Remove the credential from the set for the specified protection space.
@@ -63,8 +99,10 @@ open class URLCredentialStorage: NSObject {
63
99
has a persistence policy of URLCredential.Persistence.synchronizable will fail.
64
100
See removeCredential:forProtectionSpace:options.
65
101
*/
66
- open func remove( _ credential: URLCredential , for space: URLProtectionSpace ) { NSUnimplemented ( ) }
67
-
102
+ open func remove( _ credential: URLCredential , for space: URLProtectionSpace ) {
103
+ remove ( credential, for: space, options: nil )
104
+ }
105
+
68
106
/*!
69
107
@method removeCredential:forProtectionSpace:options
70
108
@abstract Remove the credential from the set for the specified protection space based on options.
@@ -76,31 +114,126 @@ open class URLCredentialStorage: NSObject {
76
114
are removed, the credential will be removed on all devices that contain this credential.
77
115
@discussion The credential is removed from both persistent and temporary storage.
78
116
*/
79
- open func remove( _ credential: URLCredential , for space: URLProtectionSpace , options: [ String : AnyObject ] ? = [ : ] ) { NSUnimplemented ( ) }
80
-
117
+ open func remove( _ credential: URLCredential , for space: URLProtectionSpace , options: [ String : AnyObject ] ? = [ : ] ) {
118
+ if credential. persistence == . synchronizable {
119
+ guard let options = options,
120
+ let removeSynchronizable = options [ NSURLCredentialStorageRemoveSynchronizableCredentials] as? NSNumber ,
121
+ removeSynchronizable. boolValue == true else {
122
+ return
123
+ }
124
+ }
125
+
126
+ var needsNotification = false
127
+
128
+ _lock. lock ( )
129
+
130
+ if let user = credential. user {
131
+ if _credentials [ space] ? [ user] == credential {
132
+ _credentials [ space] ? [ user] = nil
133
+ needsNotification = true
134
+ // If we remove the last entry, remove the protection space.
135
+ if _credentials [ space] ? . count == 0 {
136
+ _credentials [ space] = nil
137
+ }
138
+ }
139
+ }
140
+ // Also, look for a default object, if it exists, but check equality.
141
+ if let defaultCredential = _defaultCredentials [ space] ,
142
+ defaultCredential == credential {
143
+ _defaultCredentials [ space] = nil
144
+ needsNotification = true
145
+ }
146
+
147
+ _lock. unlock ( )
148
+
149
+ if needsNotification {
150
+ _sendNotificationWhileUnlocked ( )
151
+ }
152
+ }
153
+
81
154
/*!
82
155
@method defaultCredentialForProtectionSpace:
83
156
@abstract Get the default credential for the specified protection space.
84
157
@param space The protection space for which to get the default credential.
85
158
*/
86
- open func defaultCredential( for space: URLProtectionSpace ) -> URLCredential ? { NSUnimplemented ( ) }
87
-
159
+ open func defaultCredential( for space: URLProtectionSpace ) -> URLCredential ? {
160
+ _lock. lock ( )
161
+ defer { _lock. unlock ( ) }
162
+
163
+ return _defaultCredentials [ space]
164
+ }
165
+
88
166
/*!
89
167
@method setDefaultCredential:forProtectionSpace:
90
168
@abstract Set the default credential for the specified protection space.
91
169
@param credential The credential to set as default.
92
170
@param space The protection space for which the credential should be set as default.
93
171
@discussion If the credential is not yet in the set for the protection space, it will be added to it.
94
172
*/
95
- open func setDefaultCredential( _ credential: URLCredential , for space: URLProtectionSpace ) { NSUnimplemented ( ) }
173
+ open func setDefaultCredential( _ credential: URLCredential , for space: URLProtectionSpace ) {
174
+ guard credential. persistence != . synchronizable else {
175
+ NSUnimplemented ( )
176
+ }
177
+
178
+ guard credential. persistence != . none else {
179
+ return
180
+ }
181
+
182
+ _lock. lock ( )
183
+ let needsNotification = _setWhileLocked ( credential, for: space, isDefault: true )
184
+ _lock. unlock ( )
185
+
186
+ if needsNotification {
187
+ _sendNotificationWhileUnlocked ( )
188
+ }
189
+ }
190
+
191
+ private func _setWhileLocked( _ credential: URLCredential , for space: URLProtectionSpace , isDefault: Bool = false ) -> Bool {
192
+ var modified = false
193
+
194
+ if let user = credential. user {
195
+ if _credentials [ space] == nil {
196
+ _credentials [ space] = [ : ]
197
+ }
198
+
199
+ modified = _credentials [ space] ![ user] != credential
200
+ _credentials [ space] ![ user] = credential
201
+ }
202
+
203
+ if isDefault || _defaultCredentials [ space] == nil {
204
+ modified = modified || _defaultCredentials [ space] != credential
205
+ _defaultCredentials [ space] = credential
206
+ }
207
+
208
+ return modified
209
+ }
210
+
211
+ private func _sendNotificationWhileUnlocked( ) {
212
+ let notification = Notification ( name: . NSURLCredentialStorageChanged, object: self , userInfo: nil )
213
+ NotificationCenter . default. post ( notification)
214
+ }
96
215
}
97
216
98
217
extension URLCredentialStorage {
99
- public func getCredentials( for protectionSpace: URLProtectionSpace , task: URLSessionTask , completionHandler: ( [ String : URLCredential ] ? ) -> Void ) { NSUnimplemented ( ) }
100
- public func set( _ credential: URLCredential , for protectionSpace: URLProtectionSpace , task: URLSessionTask ) { NSUnimplemented ( ) }
101
- public func remove( _ credential: URLCredential , for protectionSpace: URLProtectionSpace , options: [ String : AnyObject ] ? = [ : ] , task: URLSessionTask ) { NSUnimplemented ( ) }
102
- public func getDefaultCredential( for space: URLProtectionSpace , task: URLSessionTask , completionHandler: ( URLCredential ? ) -> Void ) { NSUnimplemented ( ) }
103
- public func setDefaultCredential( _ credential: URLCredential , for protectionSpace: URLProtectionSpace , task: URLSessionTask ) { NSUnimplemented ( ) }
218
+ public func getCredentials( for protectionSpace: URLProtectionSpace , task: URLSessionTask , completionHandler: ( [ String : URLCredential ] ? ) -> Void ) {
219
+ completionHandler ( credentials ( for: protectionSpace) )
220
+ }
221
+
222
+ public func set( _ credential: URLCredential , for protectionSpace: URLProtectionSpace , task: URLSessionTask ) {
223
+ set ( credential, for: protectionSpace)
224
+ }
225
+
226
+ public func remove( _ credential: URLCredential , for protectionSpace: URLProtectionSpace , options: [ String : AnyObject ] ? = [ : ] , task: URLSessionTask ) {
227
+ remove ( credential, for: protectionSpace, options: options)
228
+ }
229
+
230
+ public func getDefaultCredential( for space: URLProtectionSpace , task: URLSessionTask , completionHandler: ( URLCredential ? ) -> Void ) {
231
+ completionHandler ( defaultCredential ( for: space) )
232
+ }
233
+
234
+ public func setDefaultCredential( _ credential: URLCredential , for protectionSpace: URLProtectionSpace , task: URLSessionTask ) {
235
+ setDefaultCredential ( credential, for: protectionSpace)
236
+ }
104
237
}
105
238
106
239
extension Notification . Name {
@@ -119,4 +252,3 @@ extension Notification.Name {
119
252
* to remove such a credential.
120
253
*/
121
254
public let NSURLCredentialStorageRemoveSynchronizableCredentials : String = " NSURLCredentialStorageRemoveSynchronizableCredentials "
122
-
0 commit comments