Showing
2 changed files
with
93 additions
and
35 deletions
| @@ -962,23 +962,22 @@ static NSOperationQueue *sharedQueue = nil; | @@ -962,23 +962,22 @@ static NSOperationQueue *sharedQueue = nil; | ||
| 962 | return; | 962 | return; |
| 963 | } | 963 | } |
| 964 | 964 | ||
| 965 | - // First, see if we have any credentials we can use in the session store | ||
| 966 | NSDictionary *credentials = nil; | 965 | NSDictionary *credentials = nil; |
| 967 | - if ([self useSessionPersistence]) { | ||
| 968 | - credentials = [self findSessionAuthenticationCredentials]; | ||
| 969 | - } | ||
| 970 | - | ||
| 971 | 966 | ||
| 972 | - // Are any credentials set on this request that might be used for basic authentication? | 967 | + // Do we already have an auth header? |
| 973 | - if ([self username] && [self password] && ![self domain]) { | 968 | + if (![[self requestHeaders] objectForKey:@"Authorization"]) { |
| 974 | 969 | ||
| 975 | - // If we know this request should use Basic auth, we'll add an Authorization header with basic credentials | 970 | + // If we have basic authentication explicitly set and a username and password set on the request, add a basic auth header |
| 976 | - if ([[self authenticationScheme] isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeBasic]) { | 971 | + if ([self username] && [self password] && [[self authenticationScheme] isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeBasic]) { |
| 977 | [self addBasicAuthenticationHeaderWithUsername:[self username] andPassword:[self password]]; | 972 | [self addBasicAuthenticationHeaderWithUsername:[self username] andPassword:[self password]]; |
| 978 | - } | ||
| 979 | - } | ||
| 980 | 973 | ||
| 981 | - if (credentials && ![[self requestHeaders] objectForKey:@"Authorization"]) { | 974 | + } else { |
| 975 | + | ||
| 976 | + // See if we have any cached credentials we can use in the session store | ||
| 977 | + if ([self useSessionPersistence]) { | ||
| 978 | + credentials = [self findSessionAuthenticationCredentials]; | ||
| 979 | + | ||
| 980 | + if (credentials) { | ||
| 982 | 981 | ||
| 983 | // When the Authentication key is set, the credentials were stored after an authentication challenge, so we can let CFNetwork apply them | 982 | // When the Authentication key is set, the credentials were stored after an authentication challenge, so we can let CFNetwork apply them |
| 984 | // (credentials for Digest and NTLM will always be stored like this) | 983 | // (credentials for Digest and NTLM will always be stored like this) |
| @@ -996,6 +995,11 @@ static NSOperationQueue *sharedQueue = nil; | @@ -996,6 +995,11 @@ static NSOperationQueue *sharedQueue = nil; | ||
| 996 | [self addBasicAuthenticationHeaderWithUsername:[usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationUsername] andPassword:[usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationPassword]]; | 995 | [self addBasicAuthenticationHeaderWithUsername:[usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationUsername] andPassword:[usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationPassword]]; |
| 997 | } | 996 | } |
| 998 | } | 997 | } |
| 998 | + } | ||
| 999 | + } | ||
| 1000 | + } | ||
| 1001 | + | ||
| 1002 | + // Apply proxy authentication credentials | ||
| 999 | if ([self useSessionPersistence]) { | 1003 | if ([self useSessionPersistence]) { |
| 1000 | credentials = [self findSessionProxyAuthenticationCredentials]; | 1004 | credentials = [self findSessionProxyAuthenticationCredentials]; |
| 1001 | if (credentials) { | 1005 | if (credentials) { |
| @@ -2394,11 +2398,11 @@ static NSOperationQueue *sharedQueue = nil; | @@ -2394,11 +2398,11 @@ static NSOperationQueue *sharedQueue = nil; | ||
| 2394 | NSString *user = nil; | 2398 | NSString *user = nil; |
| 2395 | NSString *pass = nil; | 2399 | NSString *pass = nil; |
| 2396 | 2400 | ||
| 2397 | - | 2401 | + ASIHTTPRequest *theRequest = [self mainRequest]; |
| 2398 | // If this is a HEAD request generated by an ASINetworkQueue, we'll try to use the details from the main request | 2402 | // If this is a HEAD request generated by an ASINetworkQueue, we'll try to use the details from the main request |
| 2399 | - if ([self mainRequest] && [[self mainRequest] proxyUsername] && [[self mainRequest] proxyPassword]) { | 2403 | + if ([theRequest proxyUsername] && [theRequest proxyPassword]) { |
| 2400 | - user = [[self mainRequest] proxyUsername]; | 2404 | + user = [theRequest proxyUsername]; |
| 2401 | - pass = [[self mainRequest] proxyPassword]; | 2405 | + pass = [theRequest proxyPassword]; |
| 2402 | 2406 | ||
| 2403 | // Let's try to use the ones set in this object | 2407 | // Let's try to use the ones set in this object |
| 2404 | } else if ([self proxyUsername] && [self proxyPassword]) { | 2408 | } else if ([self proxyUsername] && [self proxyPassword]) { |
| @@ -2406,6 +2410,13 @@ static NSOperationQueue *sharedQueue = nil; | @@ -2406,6 +2410,13 @@ static NSOperationQueue *sharedQueue = nil; | ||
| 2406 | pass = [self proxyPassword]; | 2410 | pass = [self proxyPassword]; |
| 2407 | } | 2411 | } |
| 2408 | 2412 | ||
| 2413 | + // When we connect to a website using NTLM via a proxy, we will use the main credentials | ||
| 2414 | + if ((!user || !pass) && [self proxyAuthenticationScheme] == (NSString *)kCFHTTPAuthenticationSchemeNTLM) { | ||
| 2415 | + user = [self username]; | ||
| 2416 | + pass = [self password]; | ||
| 2417 | + } | ||
| 2418 | + | ||
| 2419 | + | ||
| 2409 | 2420 | ||
| 2410 | // Ok, that didn't work, let's try the keychain | 2421 | // Ok, that didn't work, let's try the keychain |
| 2411 | // For authenticating proxies, we'll look in the keychain regardless of the value of useKeychainPersistence | 2422 | // For authenticating proxies, we'll look in the keychain regardless of the value of useKeychainPersistence |
| @@ -2423,13 +2434,21 @@ static NSOperationQueue *sharedQueue = nil; | @@ -2423,13 +2434,21 @@ static NSOperationQueue *sharedQueue = nil; | ||
| 2423 | 2434 | ||
| 2424 | NSString *ntlmDomain = [self proxyDomain]; | 2435 | NSString *ntlmDomain = [self proxyDomain]; |
| 2425 | 2436 | ||
| 2426 | - // If we have no domain yet, let's try to extract it from the username | 2437 | + // If we have no domain yet |
| 2427 | if (!ntlmDomain || [ntlmDomain length] == 0) { | 2438 | if (!ntlmDomain || [ntlmDomain length] == 0) { |
| 2428 | - ntlmDomain = @""; | 2439 | + |
| 2440 | + // Let's try to extract it from the username | ||
| 2429 | NSArray* ntlmComponents = [user componentsSeparatedByString:@"\\"]; | 2441 | NSArray* ntlmComponents = [user componentsSeparatedByString:@"\\"]; |
| 2430 | if ([ntlmComponents count] == 2) { | 2442 | if ([ntlmComponents count] == 2) { |
| 2431 | ntlmDomain = [ntlmComponents objectAtIndex:0]; | 2443 | ntlmDomain = [ntlmComponents objectAtIndex:0]; |
| 2432 | user = [ntlmComponents objectAtIndex:1]; | 2444 | user = [ntlmComponents objectAtIndex:1]; |
| 2445 | + | ||
| 2446 | + // If we are connecting to a website using NTLM, but we are connecting via a proxy, the string we need may be in the domain property | ||
| 2447 | + } else { | ||
| 2448 | + ntlmDomain = [self domain]; | ||
| 2449 | + } | ||
| 2450 | + if (!ntlmDomain) { | ||
| 2451 | + ntlmDomain = @""; | ||
| 2433 | } | 2452 | } |
| 2434 | } | 2453 | } |
| 2435 | [newCredentials setObject:ntlmDomain forKey:(NSString *)kCFHTTPAuthenticationAccountDomain]; | 2454 | [newCredentials setObject:ntlmDomain forKey:(NSString *)kCFHTTPAuthenticationAccountDomain]; |
| @@ -2815,6 +2834,7 @@ static NSOperationQueue *sharedQueue = nil; | @@ -2815,6 +2834,7 @@ static NSOperationQueue *sharedQueue = nil; | ||
| 2815 | return; | 2834 | return; |
| 2816 | } | 2835 | } |
| 2817 | 2836 | ||
| 2837 | + // Do we actually need to authenticate with a proxy? | ||
| 2818 | if ([self authenticationNeeded] == ASIProxyAuthenticationNeeded) { | 2838 | if ([self authenticationNeeded] == ASIProxyAuthenticationNeeded) { |
| 2819 | [self attemptToApplyProxyCredentialsAndResume]; | 2839 | [self attemptToApplyProxyCredentialsAndResume]; |
| 2820 | return; | 2840 | return; |
| @@ -4005,31 +4025,59 @@ static NSOperationQueue *sharedQueue = nil; | @@ -4005,31 +4025,59 @@ static NSOperationQueue *sharedQueue = nil; | ||
| 4005 | { | 4025 | { |
| 4006 | [sessionCredentialsLock lock]; | 4026 | [sessionCredentialsLock lock]; |
| 4007 | NSMutableArray *sessionCredentialsList = [[self class] sessionCredentialsStore]; | 4027 | NSMutableArray *sessionCredentialsList = [[self class] sessionCredentialsStore]; |
| 4008 | - // Find an exact match (same url) | 4028 | + NSURL *requestURL = [self url]; |
| 4029 | + | ||
| 4030 | + BOOL haveFoundExactMatch; | ||
| 4031 | + NSDictionary *closeMatch = nil; | ||
| 4032 | + | ||
| 4033 | + // Loop through all the cached credentials we have, looking for the best match for this request | ||
| 4009 | for (NSDictionary *theCredentials in sessionCredentialsList) { | 4034 | for (NSDictionary *theCredentials in sessionCredentialsList) { |
| 4010 | - if ([(NSURL*)[theCredentials objectForKey:@"URL"] isEqual:[self url]]) { | 4035 | + |
| 4011 | - // /Just a sanity check to ensure we never choose credentials from a different realm. Can't really do more than that, as either this request or the stored credentials may not have a realm when the other does | 4036 | + haveFoundExactMatch = NO; |
| 4012 | - if (![self responseStatusCode] || (![theCredentials objectForKey:@"AuthenticationRealm"] || [[theCredentials objectForKey:@"AuthenticationRealm"] isEqualToString:[self authenticationRealm]])) { | 4037 | + NSURL *cachedCredentialsURL = [theCredentials objectForKey:@"URL"]; |
| 4013 | - [sessionCredentialsLock unlock]; | 4038 | + |
| 4014 | - return theCredentials; | 4039 | + // Find an exact match (same url) |
| 4040 | + if ([cachedCredentialsURL isEqual:[self url]]) { | ||
| 4041 | + haveFoundExactMatch = YES; | ||
| 4042 | + | ||
| 4043 | + // This is not an exact match for the url, and we already have a close match we can use | ||
| 4044 | + } else if (closeMatch) { | ||
| 4045 | + continue; | ||
| 4046 | + | ||
| 4047 | + // Find a close match (same host, scheme and port) | ||
| 4048 | + } else if ([[cachedCredentialsURL host] isEqualToString:[requestURL host]] && ([cachedCredentialsURL port] == [requestURL port] || ([requestURL port] && [[cachedCredentialsURL port] isEqualToNumber:[requestURL port]])) && [[cachedCredentialsURL scheme] isEqualToString:[requestURL scheme]]) { | ||
| 4049 | + } else { | ||
| 4050 | + continue; | ||
| 4015 | } | 4051 | } |
| 4052 | + | ||
| 4053 | + // Just a sanity check to ensure we never choose credentials from a different realm. Can't really do more than that, as either this request or the stored credentials may not have a realm when the other does | ||
| 4054 | + if ([self authenticationRealm] && ([theCredentials objectForKey:@"AuthenticationRealm"] && ![[theCredentials objectForKey:@"AuthenticationRealm"] isEqualToString:[self authenticationRealm]])) { | ||
| 4055 | + continue; | ||
| 4056 | + } | ||
| 4057 | + | ||
| 4058 | + // If we have a username and password set on the request, check that they are the same as the cached ones | ||
| 4059 | + if ([self username] && [self password]) { | ||
| 4060 | + NSDictionary *usernameAndPassword = [theCredentials objectForKey:@"Credentials"]; | ||
| 4061 | + NSString *storedUsername = [usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationUsername]; | ||
| 4062 | + NSString *storedPassword = [usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationUsername]; | ||
| 4063 | + if (![storedUsername isEqualToString:[self username]] || ![storedPassword isEqualToString:[self password]]) { | ||
| 4064 | + continue; | ||
| 4016 | } | 4065 | } |
| 4017 | } | 4066 | } |
| 4018 | - // Find a rough match (same host, port, scheme) | ||
| 4019 | - NSURL *requestURL = [self url]; | ||
| 4020 | - for (NSDictionary *theCredentials in sessionCredentialsList) { | ||
| 4021 | - NSURL *theURL = [theCredentials objectForKey:@"URL"]; | ||
| 4022 | 4067 | ||
| 4023 | - // Port can be nil! | 4068 | + // If we have an exact match for the url, use those credentials |
| 4024 | - if ([[theURL host] isEqualToString:[requestURL host]] && ([theURL port] == [requestURL port] || ([requestURL port] && [[theURL port] isEqualToNumber:[requestURL port]])) && [[theURL scheme] isEqualToString:[requestURL scheme]]) { | 4069 | + if (haveFoundExactMatch) { |
| 4025 | - if (![self responseStatusCode] || (![theCredentials objectForKey:@"AuthenticationRealm"] || [[theCredentials objectForKey:@"AuthenticationRealm"] isEqualToString:[self authenticationRealm]])) { | ||
| 4026 | [sessionCredentialsLock unlock]; | 4070 | [sessionCredentialsLock unlock]; |
| 4027 | return theCredentials; | 4071 | return theCredentials; |
| 4028 | } | 4072 | } |
| 4029 | - } | 4073 | + |
| 4074 | + // We have no exact match, let's remember that we have a good match for this server, and we'll use it at the end if we don't find an exact match | ||
| 4075 | + closeMatch = theCredentials; | ||
| 4030 | } | 4076 | } |
| 4031 | [sessionCredentialsLock unlock]; | 4077 | [sessionCredentialsLock unlock]; |
| 4032 | - return nil; | 4078 | + |
| 4079 | + // Return credentials that matched on host, port and scheme, or nil if we didn't find any | ||
| 4080 | + return closeMatch; | ||
| 4033 | } | 4081 | } |
| 4034 | 4082 | ||
| 4035 | #pragma mark keychain storage | 4083 | #pragma mark keychain storage |
| @@ -1087,6 +1087,16 @@ | @@ -1087,6 +1087,16 @@ | ||
| 1087 | err = [request error]; | 1087 | err = [request error]; |
| 1088 | GHAssertNil(err,@"Failed to reuse credentials"); | 1088 | GHAssertNil(err,@"Failed to reuse credentials"); |
| 1089 | 1089 | ||
| 1090 | + // Ensure new credentials are used in place of those in the session | ||
| 1091 | + request = [[[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/basic-authentication-new-credentials"]] autorelease]; | ||
| 1092 | + [request setUsername:@"secret_username_2"]; | ||
| 1093 | + [request setPassword:@"secret_password_2"]; | ||
| 1094 | + [request setUseSessionPersistence:YES]; | ||
| 1095 | + [request setUseKeychainPersistence:NO]; | ||
| 1096 | + [request startSynchronous]; | ||
| 1097 | + err = [request error]; | ||
| 1098 | + GHAssertNil(err,@"Failed to reuse credentials"); | ||
| 1099 | + | ||
| 1090 | [ASIHTTPRequest clearSession]; | 1100 | [ASIHTTPRequest clearSession]; |
| 1091 | 1101 | ||
| 1092 | // Ensure credentials stored in the session were wiped | 1102 | // Ensure credentials stored in the session were wiped |
| @@ -1103,7 +1113,7 @@ | @@ -1103,7 +1113,7 @@ | ||
| 1103 | err = [request error]; | 1113 | err = [request error]; |
| 1104 | GHAssertNil(err,@"Failed to use stored credentials"); | 1114 | GHAssertNil(err,@"Failed to use stored credentials"); |
| 1105 | 1115 | ||
| 1106 | - [ASIHTTPRequest removeCredentialsForHost:@"allseeing-i.com" port:0 protocol:@"http" realm:@"SECRET_STUFF"]; | 1116 | + [ASIHTTPRequest removeCredentialsForHost:@"asi" port:0 protocol:@"http" realm:@"SECRET_STUFF"]; |
| 1107 | 1117 | ||
| 1108 | // Ensure credentials stored in the keychain were wiped | 1118 | // Ensure credentials stored in the keychain were wiped |
| 1109 | request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; | 1119 | request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; |
| @@ -1424,7 +1434,7 @@ | @@ -1424,7 +1434,7 @@ | ||
| 1424 | { | 1434 | { |
| 1425 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect_to_new_domain"]]; | 1435 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/redirect_to_new_domain"]]; |
| 1426 | [request startSynchronous]; | 1436 | [request startSynchronous]; |
| 1427 | - BOOL success = [[[request url] absoluteString] isEqualToString:@"http://www.apple.com/"]; | 1437 | + BOOL success = [[[request url] absoluteString] isEqualToString:@"http://www.apple.com"]; |
| 1428 | GHAssertTrue(success,@"Failed to redirect to a different domain"); | 1438 | GHAssertTrue(success,@"Failed to redirect to a different domain"); |
| 1429 | } | 1439 | } |
| 1430 | 1440 |
-
Please register or login to post a comment