Ben Copsey

Refactor session credentials storage to store multiple sets of credentials, and …

…perform some checking to see if we should apply the credentials
Tweak test
... ... @@ -388,8 +388,8 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
#pragma mark http authentication stuff
// Apply credentials to this request
- (BOOL)applyCredentials:(NSMutableDictionary *)newCredentials;
- (BOOL)applyProxyCredentials:(NSMutableDictionary *)newCredentials;
- (BOOL)applyCredentials:(NSDictionary *)newCredentials;
- (BOOL)applyProxyCredentials:(NSDictionary *)newCredentials;
// Attempt to obtain credentials for this request from the URL, username and password or keychain
- (NSMutableDictionary *)findCredentials;
... ... @@ -414,17 +414,25 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
- (void)handleStreamComplete;
- (void)handleStreamError;
#pragma mark managing the session
# pragma mark session credentials
+ (NSMutableArray *)sessionProxyCredentialsStore;
+ (NSMutableArray *)sessionCredentialsStore;
+ (void)storeProxyAuthenticationCredentialsInSessionStore:(NSDictionary *)credentials;
+ (void)storeAuthenticationCredentialsInSessionStore:(NSDictionary *)credentials;
+ (void)removeProxyAuthenticationCredentialsFromSessionStore:(NSDictionary *)credentials;
+ (void)removeAuthenticationCredentialsFromSessionStore:(NSDictionary *)credentials;
- (NSDictionary *)findSessionProxyAuthenticationCredentials;
- (NSDictionary *)findSessionAuthenticationCredentials;
+ (void)setSessionCredentials:(NSMutableDictionary *)newCredentials;
+ (void)setSessionAuthentication:(CFHTTPAuthenticationRef)newAuthentication;
+ (void)setSessionProxyCredentials:(NSMutableDictionary *)newCredentials;
+ (void)setSessionProxyAuthentication:(CFHTTPAuthenticationRef)newAuthentication;
#pragma mark keychain storage
// Save credentials for this request to the keychain
- (void)saveCredentialsToKeychain:(NSMutableDictionary *)newCredentials;
- (void)saveCredentialsToKeychain:(NSDictionary *)newCredentials;
// Save credentials to the keychain
+ (void)saveCredentials:(NSURLCredential *)credentials forHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm;
... ...
... ... @@ -28,11 +28,8 @@ NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";
static const CFOptionFlags kNetworkEvents = kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred;
static CFHTTPAuthenticationRef sessionAuthentication = NULL;
static NSMutableDictionary *sessionCredentials = nil;
static CFHTTPAuthenticationRef sessionProxyAuthentication = NULL;
static NSMutableDictionary *sessionProxyCredentials = nil;
static NSMutableArray *sessionCredentialsStore = nil;
static NSMutableArray *sessionProxyCredentialsStore = nil;
static NSMutableArray *sessionCookies = nil;
... ... @@ -147,7 +144,6 @@ static NSRecursiveLock *delegateAuthenticationLock = nil;
ASIRequestCancelledError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIRequestCancelledErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request was cancelled",NSLocalizedDescriptionKey,nil]] retain];
ASIUnableToCreateRequestError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIUnableToCreateRequestErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to create request (bad url?)",NSLocalizedDescriptionKey,nil]] retain];
ASITooMuchRedirectionError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASITooMuchRedirectionErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request failed because it redirected too many times",NSLocalizedDescriptionKey,nil]] retain];
}
[super initialize];
}
... ... @@ -430,16 +426,16 @@ static NSRecursiveLock *delegateAuthenticationLock = nil;
// If we've already talked to this server and have valid credentials, let's apply them to the request
if ([self useSessionPersistance]) {
if (sessionCredentials && sessionAuthentication) {
if (!CFHTTPMessageApplyCredentialDictionary(request, sessionAuthentication, (CFMutableDictionaryRef)sessionCredentials, NULL)) {
[ASIHTTPRequest setSessionAuthentication:NULL];
[ASIHTTPRequest setSessionCredentials:nil];
NSDictionary *credentials = [self findSessionAuthenticationCredentials];
if (credentials) {
if (!CFHTTPMessageApplyCredentialDictionary(request, (CFHTTPAuthenticationRef)[credentials objectForKey:@"Authentication"], (CFDictionaryRef)[credentials objectForKey:@"Credentials"], NULL)) {
[[self class] removeAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]];
}
}
if (sessionProxyCredentials && sessionProxyAuthentication) {
if (!CFHTTPMessageApplyCredentialDictionary(request, sessionProxyAuthentication, (CFMutableDictionaryRef)sessionProxyCredentials, NULL)) {
[ASIHTTPRequest setSessionProxyAuthentication:NULL];
[ASIHTTPRequest setSessionProxyCredentials:nil];
credentials = [self findSessionProxyAuthenticationCredentials];
if (credentials) {
if (!CFHTTPMessageApplyCredentialDictionary(request, (CFHTTPAuthenticationRef)[credentials objectForKey:@"Authentication"], (CFDictionaryRef)[credentials objectForKey:@"Credentials"], NULL)) {
[[self class] removeProxyAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]];
}
}
}
... ... @@ -1255,7 +1251,7 @@ static NSRecursiveLock *delegateAuthenticationLock = nil;
#pragma mark http authentication
- (void)saveProxyCredentialsToKeychain:(NSMutableDictionary *)newCredentials
- (void)saveProxyCredentialsToKeychain:(NSDictionary *)newCredentials
{
NSURLCredential *authenticationCredentials = [NSURLCredential credentialWithUser:[newCredentials objectForKey:(NSString *)kCFHTTPAuthenticationUsername] password:[newCredentials objectForKey:(NSString *)kCFHTTPAuthenticationPassword] persistence:NSURLCredentialPersistencePermanent];
if (authenticationCredentials) {
... ... @@ -1264,7 +1260,7 @@ static NSRecursiveLock *delegateAuthenticationLock = nil;
}
- (void)saveCredentialsToKeychain:(NSMutableDictionary *)newCredentials
- (void)saveCredentialsToKeychain:(NSDictionary *)newCredentials
{
NSURLCredential *authenticationCredentials = [NSURLCredential credentialWithUser:[newCredentials objectForKey:(NSString *)kCFHTTPAuthenticationUsername] password:[newCredentials objectForKey:(NSString *)kCFHTTPAuthenticationPassword] persistence:NSURLCredentialPersistencePermanent];
... ... @@ -1273,7 +1269,7 @@ static NSRecursiveLock *delegateAuthenticationLock = nil;
}
}
- (BOOL)applyProxyCredentials:(NSMutableDictionary *)newCredentials
- (BOOL)applyProxyCredentials:(NSDictionary *)newCredentials
{
[self setProxyAuthenticationRetryCount:[self proxyAuthenticationRetryCount]+1];
... ... @@ -1287,20 +1283,24 @@ static NSRecursiveLock *delegateAuthenticationLock = nil;
[self saveProxyCredentialsToKeychain:newCredentials];
}
if (useSessionPersistance) {
[ASIHTTPRequest setSessionProxyAuthentication:proxyAuthentication];
[ASIHTTPRequest setSessionProxyCredentials:newCredentials];
NSMutableDictionary *sessionProxyCredentials = [NSMutableDictionary dictionary];
[sessionProxyCredentials setObject:(id)proxyAuthentication forKey:@"Authentication"];
[sessionProxyCredentials setObject:newCredentials forKey:@"Credentials"];
[sessionProxyCredentials setObject:[self proxyHost] forKey:@"Host"];
[sessionProxyCredentials setObject:[NSNumber numberWithInt:[self proxyPort]] forKey:@"Port"];
[sessionProxyCredentials setObject:[self proxyAuthenticationScheme] forKey:@"AuthenticationScheme"];
[[self class] storeProxyAuthenticationCredentialsInSessionStore:sessionProxyCredentials];
}
[self setProxyCredentials:newCredentials];
return YES;
} else {
[ASIHTTPRequest setSessionAuthentication:NULL];
[ASIHTTPRequest setSessionCredentials:nil];
[[self class] removeProxyAuthenticationCredentialsFromSessionStore:newCredentials];
}
}
return NO;
}
- (BOOL)applyCredentials:(NSMutableDictionary *)newCredentials
- (BOOL)applyCredentials:(NSDictionary *)newCredentials
{
[self setAuthenticationRetryCount:[self authenticationRetryCount]+1];
... ... @@ -1313,11 +1313,22 @@ static NSRecursiveLock *delegateAuthenticationLock = nil;
[self saveCredentialsToKeychain:newCredentials];
}
if (useSessionPersistance) {
[ASIHTTPRequest setSessionAuthentication:requestAuthentication];
[ASIHTTPRequest setSessionCredentials:newCredentials];
NSMutableDictionary *sessionCredentials = [NSMutableDictionary dictionary];
[sessionCredentials setObject:(id)requestAuthentication forKey:@"Authentication"];
[sessionCredentials setObject:newCredentials forKey:@"Credentials"];
[sessionCredentials setObject:[self url] forKey:@"URL"];
[sessionCredentials setObject:[self authenticationScheme] forKey:@"AuthenticationScheme"];
if ([self authenticationRealm]) {
[sessionCredentials setObject:[self authenticationRealm] forKey:@"AuthenticationRealm"];
}
[[self class] storeAuthenticationCredentialsInSessionStore:sessionCredentials];
}
[self setRequestCredentials:newCredentials];
return YES;
} else {
[[self class] removeAuthenticationCredentialsFromSessionStore:newCredentials];
}
}
return NO;
... ... @@ -1536,23 +1547,26 @@ static NSRecursiveLock *delegateAuthenticationLock = nil;
// Prevent more than one request from asking for credentials at once
[delegateAuthenticationLock lock];
// We know the credentials we just presented are bad. If they are the same as the session credentials, we should clear those too.
if ([self proxyCredentials] == sessionProxyCredentials) {
[ASIHTTPRequest setSessionProxyCredentials:nil];
}
// We know the credentials we just presented are bad, we should remove them from the session store too
[[self class] removeProxyAuthenticationCredentialsFromSessionStore:proxyCredentials];
[self setProxyCredentials:nil];
// If the user cancelled authentication via a dialog presented by another request, our queue may have cancelled us
if ([self error] || [self isCancelled]) {
[delegateAuthenticationLock unlock];
return;
}
// Now we've aquirred the lock, it may be that the session contains credentials we can re-use for this request
if ([self useSessionPersistance] && sessionProxyCredentials && [self applyProxyCredentials:sessionProxyCredentials]) {
[delegateAuthenticationLock unlock];
[self startRequest];
return;
// Now we've acquired the lock, it may be that the session contains credentials we can re-use for this request
if ([self useSessionPersistance]) {
NSDictionary *credentials = [self findSessionProxyAuthenticationCredentials];
if (credentials && [self applyProxyCredentials:[credentials objectForKey:@"Credentials"]]) {
[delegateAuthenticationLock unlock];
[self startRequest];
return;
}
}
[self setLastActivityTime:nil];
... ... @@ -1603,11 +1617,14 @@ static NSRecursiveLock *delegateAuthenticationLock = nil;
return;
}
// Now we've aquirred the lock, it may be that the session contains credentials we can re-use for this request
if ([self useSessionPersistance] && sessionProxyCredentials && [self applyProxyCredentials:sessionProxyCredentials]) {
[delegateAuthenticationLock unlock];
[self startRequest];
return;
// Now we've acquired the lock, it may be that the session contains credentials we can re-use for this request
if ([self useSessionPersistance]) {
NSDictionary *credentials = [self findSessionProxyAuthenticationCredentials];
if (credentials && [self applyProxyCredentials:[credentials objectForKey:@"Credentials"]]) {
[delegateAuthenticationLock unlock];
[self startRequest];
return;
}
}
NSMutableDictionary *newCredentials = [self findProxyCredentials];
... ... @@ -1732,10 +1749,8 @@ static NSRecursiveLock *delegateAuthenticationLock = nil;
// Prevent more than one request from asking for credentials at once
[delegateAuthenticationLock lock];
// We know the credentials we just presented are bad. If they are the same as the session credentials, we should clear those too.
if ([self requestCredentials] == sessionCredentials) {
[ASIHTTPRequest setSessionCredentials:nil];
}
// We know the credentials we just presented are bad, we should remove them from the session store too
[[self class] removeAuthenticationCredentialsFromSessionStore:requestCredentials];
[self setRequestCredentials:nil];
// If the user cancelled authentication via a dialog presented by another request, our queue may have cancelled us
... ... @@ -1744,11 +1759,14 @@ static NSRecursiveLock *delegateAuthenticationLock = nil;
return;
}
// Now we've aquirred the lock, it may be that the session contains credentials we can re-use for this request
if ([self useSessionPersistance] && sessionCredentials && [self applyCredentials:sessionCredentials]) {
[delegateAuthenticationLock unlock];
[self startRequest];
return;
// Now we've acquired the lock, it may be that the session contains credentials we can re-use for this request
if ([self useSessionPersistance]) {
NSDictionary *credentials = [self findSessionAuthenticationCredentials];
if (credentials && [self applyCredentials:[credentials objectForKey:@"Credentials"]]) {
[delegateAuthenticationLock unlock];
[self startRequest];
return;
}
}
... ... @@ -1799,11 +1817,14 @@ static NSRecursiveLock *delegateAuthenticationLock = nil;
return;
}
// Now we've aquirred the lock, it may be that the session contains credentials we can re-use for this request
if ([self useSessionPersistance] && sessionCredentials && [self applyCredentials:sessionCredentials]) {
[delegateAuthenticationLock unlock];
[self startRequest];
return;
// Now we've acquired the lock, it may be that the session contains credentials we can re-use for this request
if ([self useSessionPersistance]) {
NSDictionary *credentials = [self findSessionAuthenticationCredentials];
if (credentials && [self applyCredentials:[credentials objectForKey:@"Credentials"]]) {
[delegateAuthenticationLock unlock];
[self startRequest];
return;
}
}
... ... @@ -2052,51 +2073,98 @@ static NSRecursiveLock *delegateAuthenticationLock = nil;
[super cancel];
}
#pragma mark managing the session
# pragma mark session credentials
+ (NSMutableArray *)sessionProxyCredentialsStore
{
if (!sessionProxyCredentialsStore) {
sessionProxyCredentialsStore = [[NSMutableArray alloc] init];
}
return sessionProxyCredentialsStore;
}
+ (void)setSessionCredentials:(NSMutableDictionary *)newCredentials
+ (NSMutableArray *)sessionCredentialsStore
{
if (newCredentials != sessionCredentials) {
[sessionCredentials release];
sessionCredentials = [newCredentials retain];
if (!sessionCredentialsStore) {
sessionCredentialsStore = [[NSMutableArray alloc] init];
}
return sessionCredentialsStore;
}
+ (void)setSessionAuthentication:(CFHTTPAuthenticationRef)newAuthentication
+ (void)storeProxyAuthenticationCredentialsInSessionStore:(NSDictionary *)credentials
{
if (newAuthentication != sessionAuthentication) {
if (sessionAuthentication) {
CFRelease(sessionAuthentication);
[self removeProxyAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]];
[[[self class] sessionProxyCredentialsStore] addObject:credentials];
}
+ (void)storeAuthenticationCredentialsInSessionStore:(NSDictionary *)credentials
{
[self removeAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]];
[[[self class] sessionCredentialsStore] addObject:credentials];
}
+ (void)removeProxyAuthenticationCredentialsFromSessionStore:(NSDictionary *)credentials
{
NSMutableArray *sessionCredentialsList = [[self class] sessionProxyCredentialsStore];
int i;
for (i=0; i<[sessionCredentialsList count]; i++) {
NSDictionary *theCredentials = [sessionCredentialsList objectAtIndex:i];
if ([theCredentials objectForKey:@"Credentials"] == credentials) {
[sessionCredentialsList removeObjectAtIndex:i];
return;
}
sessionAuthentication = newAuthentication;
if (newAuthentication) {
CFRetain(sessionAuthentication);
}
}
+ (void)removeAuthenticationCredentialsFromSessionStore:(NSDictionary *)credentials
{
NSMutableArray *sessionCredentialsList = [[self class] sessionCredentialsStore];
int i;
for (i=0; i<[sessionCredentialsList count]; i++) {
NSDictionary *theCredentials = [sessionCredentialsList objectAtIndex:i];
if ([theCredentials objectForKey:@"Credentials"] == credentials) {
[sessionCredentialsList removeObjectAtIndex:i];
return;
}
}
}
}
+ (void)setSessionProxyCredentials:(NSMutableDictionary *)newCredentials
- (NSDictionary *)findSessionProxyAuthenticationCredentials
{
if (newCredentials != sessionProxyCredentials) {
[sessionProxyCredentials release];
sessionProxyCredentials = [newCredentials retain];
NSMutableArray *sessionCredentialsList = [[self class] sessionProxyCredentialsStore];
for (NSDictionary *theCredentials in sessionCredentialsList) {
if ([[theCredentials objectForKey:@"Host"] isEqualTo:[self proxyHost]] && [[theCredentials objectForKey:@"Port"] intValue] == [self proxyPort]) {
return theCredentials;
}
}
return nil;
}
+ (void)setSessionProxyAuthentication:(CFHTTPAuthenticationRef)newAuthentication
- (NSDictionary *)findSessionAuthenticationCredentials
{
if (sessionProxyAuthentication != newAuthentication) {
if (sessionProxyAuthentication) {
CFRelease(sessionProxyAuthentication);
NSMutableArray *sessionCredentialsList = [[self class] sessionCredentialsStore];
// Find an exact match
for (NSDictionary *theCredentials in sessionCredentialsList) {
if ([[theCredentials objectForKey:@"URL"] isEqualTo:[self url]]) {
if (![self responseStatusCode] || [[theCredentials objectForKey:@"AuthenticationRealm"] isEqualTo:[self authenticationRealm]]) {
return theCredentials;
}
}
sessionProxyAuthentication = newAuthentication;
if (newAuthentication) {
CFRetain(sessionProxyAuthentication);
}
// Find a rough match
NSURL *requestURL = [self url];
for (NSDictionary *theCredentials in sessionCredentialsList) {
NSURL *theURL = [theCredentials objectForKey:@"URL"];
if ([[theURL host] isEqualTo:[requestURL host]] && [[theURL port] isEqualTo:[requestURL port]] && [[theURL scheme] isEqualTo:[requestURL scheme]]) {
if (![self responseStatusCode] || [[theCredentials objectForKey:@"AuthenticationRealm"] isEqualTo:[self authenticationRealm]]) {
return theCredentials;
}
}
}
return nil;
}
#pragma mark keychain storage
+ (void)saveCredentials:(NSURLCredential *)credentials forHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm
... ... @@ -2181,8 +2249,7 @@ static NSRecursiveLock *delegateAuthenticationLock = nil;
// Dump all session data (authentication and cookies)
+ (void)clearSession
{
[ASIHTTPRequest setSessionAuthentication:NULL];
[ASIHTTPRequest setSessionCredentials:nil];
[[[self class] sessionCredentialsStore] removeAllObjects];
[ASIHTTPRequest setSessionCookies:nil];
}
... ...
... ... @@ -625,7 +625,7 @@ IMPORTANT
[self setFinishedRequests:[[[NSMutableArray alloc] init] autorelease]];
[self setImmediateCancelQueue:[[[NSOperationQueue alloc] init] autorelease]];
int i;
for (i=0; i<100; i++) {
for (i=0; i<25; i++) {
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]];
[request setDelegate:self];
[request setDidFailSelector:@selector(immediateCancelFail:)];
... ... @@ -645,8 +645,8 @@ IMPORTANT
GHFail(@"A request that had already finished called its fail delegate method");
}
[[self failedRequests] addObject:request];
if ([[self failedRequests] count]+[[self finishedRequests] count] > 100) {
GHFail(@"We got more than 100 delegate fail/finish calls - this shouldn't happen!");
if ([[self failedRequests] count]+[[self finishedRequests] count] > 25) {
GHFail(@"We got more than 25 delegate fail/finish calls - this shouldn't happen!");
}
}
... ... @@ -660,8 +660,8 @@ IMPORTANT
GHFail(@"A request that had already failed called its finish delegate method");
}
[[self finishedRequests] addObject:request];
if ([[self failedRequests] count]+[[self finishedRequests] count] > 100) {
GHFail(@"We got more than 100 delegate fail/finish calls - this shouldn't happen!");
if ([[self failedRequests] count]+[[self finishedRequests] count] > 25) {
GHFail(@"We got more than 25 delegate fail/finish calls - this shouldn't happen!");
}
}
... ...