@@ -18,42 +18,74 @@ 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 != . none else {
77
+ return
78
+ }
79
+
80
+ _lock. lock ( )
81
+ let needsNotification = _setWhileLocked ( credential, for: space)
82
+ _lock. unlock ( )
83
+
84
+ if needsNotification {
85
+ _sendNotificationWhileUnlocked ( )
86
+ }
87
+ }
88
+
57
89
/*!
58
90
@method removeCredential:forProtectionSpace:
59
91
@abstract Remove the credential from the set for the specified protection space.
@@ -63,8 +95,10 @@ open class URLCredentialStorage: NSObject {
63
95
has a persistence policy of URLCredential.Persistence.synchronizable will fail.
64
96
See removeCredential:forProtectionSpace:options.
65
97
*/
66
- open func remove( _ credential: URLCredential , for space: URLProtectionSpace ) { NSUnimplemented ( ) }
67
-
98
+ open func remove( _ credential: URLCredential , for space: URLProtectionSpace ) {
99
+ remove ( credential, for: space, options: nil )
100
+ }
101
+
68
102
/*!
69
103
@method removeCredential:forProtectionSpace:options
70
104
@abstract Remove the credential from the set for the specified protection space based on options.
@@ -76,23 +110,100 @@ open class URLCredentialStorage: NSObject {
76
110
are removed, the credential will be removed on all devices that contain this credential.
77
111
@discussion The credential is removed from both persistent and temporary storage.
78
112
*/
79
- open func remove( _ credential: URLCredential , for space: URLProtectionSpace , options: [ String : AnyObject ] ? = [ : ] ) { NSUnimplemented ( ) }
80
-
113
+ open func remove( _ credential: URLCredential , for space: URLProtectionSpace , options: [ String : AnyObject ] ? = [ : ] ) {
114
+ if credential. persistence == . synchronizable {
115
+ guard let options = options,
116
+ let removeSynchronizable = options [ NSURLCredentialStorageRemoveSynchronizableCredentials] as? NSNumber ,
117
+ removeSynchronizable. boolValue == true else {
118
+ return
119
+ }
120
+ }
121
+
122
+ var needsNotification = false
123
+
124
+ _lock. lock ( )
125
+
126
+ if let user = credential. user {
127
+ if _credentials [ space] ? [ user] == credential {
128
+ _credentials [ space] ? [ user] = nil
129
+ needsNotification = true
130
+ // If we remove the last entry, remove the protection space.
131
+ if _credentials [ space] ? . count == 0 {
132
+ _credentials [ space] = nil
133
+ }
134
+ }
135
+ }
136
+ // Also, look for a default object, if it exists, but check equality.
137
+ if let defaultCredential = _defaultCredentials [ space] ,
138
+ defaultCredential == credential {
139
+ _defaultCredentials [ space] = nil
140
+ needsNotification = true
141
+ }
142
+
143
+ _lock. unlock ( )
144
+
145
+ if needsNotification {
146
+ _sendNotificationWhileUnlocked ( )
147
+ }
148
+ }
149
+
81
150
/*!
82
151
@method defaultCredentialForProtectionSpace:
83
152
@abstract Get the default credential for the specified protection space.
84
153
@param space The protection space for which to get the default credential.
85
154
*/
86
- open func defaultCredential( for space: URLProtectionSpace ) -> URLCredential ? { NSUnimplemented ( ) }
87
-
155
+ open func defaultCredential( for space: URLProtectionSpace ) -> URLCredential ? {
156
+ _lock. lock ( )
157
+ defer { _lock. unlock ( ) }
158
+
159
+ return _defaultCredentials [ space]
160
+ }
161
+
88
162
/*!
89
163
@method setDefaultCredential:forProtectionSpace:
90
164
@abstract Set the default credential for the specified protection space.
91
165
@param credential The credential to set as default.
92
166
@param space The protection space for which the credential should be set as default.
93
167
@discussion If the credential is not yet in the set for the protection space, it will be added to it.
94
168
*/
95
- open func setDefaultCredential( _ credential: URLCredential , for space: URLProtectionSpace ) { NSUnimplemented ( ) }
169
+ open func setDefaultCredential( _ credential: URLCredential , for space: URLProtectionSpace ) {
170
+ guard credential. persistence != . none else {
171
+ return
172
+ }
173
+
174
+ _lock. lock ( )
175
+ let needsNotification = _setWhileLocked ( credential, for: space, isDefault: true )
176
+ _lock. unlock ( )
177
+
178
+ if needsNotification {
179
+ _sendNotificationWhileUnlocked ( )
180
+ }
181
+ }
182
+
183
+ private func _setWhileLocked( _ credential: URLCredential , for space: URLProtectionSpace , isDefault: Bool = false ) -> Bool {
184
+ var modified = false
185
+
186
+ if let user = credential. user {
187
+ if _credentials [ space] == nil {
188
+ _credentials [ space] = [ : ]
189
+ }
190
+
191
+ modified = _credentials [ space] ![ user] != credential
192
+ _credentials [ space] ![ user] = credential
193
+ }
194
+
195
+ if isDefault || _defaultCredentials [ space] == nil {
196
+ modified = modified || _defaultCredentials [ space] != credential
197
+ _defaultCredentials [ space] = credential
198
+ }
199
+
200
+ return modified
201
+ }
202
+
203
+ private func _sendNotificationWhileUnlocked( ) {
204
+ let notification = Notification ( name: . NSURLCredentialStorageChanged, object: self , userInfo: nil )
205
+ NotificationCenter . default. post ( notification)
206
+ }
96
207
}
97
208
98
209
extension URLCredentialStorage {
@@ -119,4 +230,3 @@ extension Notification.Name {
119
230
* to remove such a credential.
120
231
*/
121
232
public let NSURLCredentialStorageRemoveSynchronizableCredentials : String = " NSURLCredentialStorageRemoveSynchronizableCredentials "
122
-
0 commit comments