Showing
2 changed files
with
116 additions
and
58 deletions
| @@ -961,41 +961,45 @@ static NSOperationQueue *sharedQueue = nil; | @@ -961,41 +961,45 @@ static NSOperationQueue *sharedQueue = nil; | ||
| 961 | if (![self shouldPresentCredentialsBeforeChallenge]) { | 961 | if (![self shouldPresentCredentialsBeforeChallenge]) { |
| 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]) { | 966 | + |
| 968 | - credentials = [self findSessionAuthenticationCredentials]; | 967 | + // Do we already have an auth header? |
| 969 | - } | 968 | + if (![[self requestHeaders] objectForKey:@"Authorization"]) { |
| 970 | - | 969 | + |
| 971 | - | 970 | + // If we have basic authentication explicitly set and a username and password set on the request, add a basic auth header |
| 972 | - // Are any credentials set on this request that might be used for basic authentication? | 971 | + if ([self username] && [self password] && [[self authenticationScheme] isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeBasic]) { |
| 973 | - if ([self username] && [self password] && ![self domain]) { | ||
| 974 | - | ||
| 975 | - // If we know this request should use Basic auth, we'll add an Authorization header with basic credentials | ||
| 976 | - if ([[self authenticationScheme] isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeBasic]) { | ||
| 977 | [self addBasicAuthenticationHeaderWithUsername:[self username] andPassword:[self password]]; | 972 | [self addBasicAuthenticationHeaderWithUsername:[self username] andPassword:[self password]]; |
| 978 | - } | 973 | + |
| 979 | - } | ||
| 980 | - | ||
| 981 | - if (credentials && ![[self requestHeaders] objectForKey:@"Authorization"]) { | ||
| 982 | - | ||
| 983 | - // 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) | ||
| 985 | - if ([credentials objectForKey:@"Authentication"]) { | ||
| 986 | - | ||
| 987 | - // If we've already talked to this server and have valid credentials, let's apply them to the request | ||
| 988 | - if (!CFHTTPMessageApplyCredentialDictionary(request, (CFHTTPAuthenticationRef)[credentials objectForKey:@"Authentication"], (CFDictionaryRef)[credentials objectForKey:@"Credentials"], NULL)) { | ||
| 989 | - [[self class] removeAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]]; | ||
| 990 | - } | ||
| 991 | - | ||
| 992 | - // If the Authentication key is not set, these credentials were stored after a username and password set on a previous request passed basic authentication | ||
| 993 | - // When this happens, we'll need to create the Authorization header ourselves | ||
| 994 | } else { | 974 | } else { |
| 995 | - NSDictionary *usernameAndPassword = [credentials objectForKey:@"Credentials"]; | 975 | + |
| 996 | - [self addBasicAuthenticationHeaderWithUsername:[usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationUsername] andPassword:[usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationPassword]]; | 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) { | ||
| 981 | + | ||
| 982 | + // When the Authentication key is set, the credentials were stored after an authentication challenge, so we can let CFNetwork apply them | ||
| 983 | + // (credentials for Digest and NTLM will always be stored like this) | ||
| 984 | + if ([credentials objectForKey:@"Authentication"]) { | ||
| 985 | + | ||
| 986 | + // If we've already talked to this server and have valid credentials, let's apply them to the request | ||
| 987 | + if (!CFHTTPMessageApplyCredentialDictionary(request, (CFHTTPAuthenticationRef)[credentials objectForKey:@"Authentication"], (CFDictionaryRef)[credentials objectForKey:@"Credentials"], NULL)) { | ||
| 988 | + [[self class] removeAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]]; | ||
| 989 | + } | ||
| 990 | + | ||
| 991 | + // If the Authentication key is not set, these credentials were stored after a username and password set on a previous request passed basic authentication | ||
| 992 | + // When this happens, we'll need to create the Authorization header ourselves | ||
| 993 | + } else { | ||
| 994 | + NSDictionary *usernameAndPassword = [credentials objectForKey:@"Credentials"]; | ||
| 995 | + [self addBasicAuthenticationHeaderWithUsername:[usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationUsername] andPassword:[usernameAndPassword objectForKey:(NSString *)kCFHTTPAuthenticationPassword]]; | ||
| 996 | + } | ||
| 997 | + } | ||
| 998 | + } | ||
| 997 | } | 999 | } |
| 998 | } | 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,17 +2398,24 @@ static NSOperationQueue *sharedQueue = nil; | @@ -2394,17 +2398,24 @@ 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]) { |
| 2405 | user = [self proxyUsername]; | 2409 | user = [self proxyUsername]; |
| 2406 | pass = [self proxyPassword]; | 2410 | pass = [self proxyPassword]; |
| 2407 | - } | 2411 | + } |
| 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 | + | ||
| 2408 | 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 |
| @@ -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) | ||
| 4009 | - for (NSDictionary *theCredentials in sessionCredentialsList) { | ||
| 4010 | - if ([(NSURL*)[theCredentials objectForKey:@"URL"] isEqual:[self url]]) { | ||
| 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 | ||
| 4012 | - if (![self responseStatusCode] || (![theCredentials objectForKey:@"AuthenticationRealm"] || [[theCredentials objectForKey:@"AuthenticationRealm"] isEqualToString:[self authenticationRealm]])) { | ||
| 4013 | - [sessionCredentialsLock unlock]; | ||
| 4014 | - return theCredentials; | ||
| 4015 | - } | ||
| 4016 | - } | ||
| 4017 | - } | ||
| 4018 | - // Find a rough match (same host, port, scheme) | ||
| 4019 | NSURL *requestURL = [self 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 | ||
| 4020 | for (NSDictionary *theCredentials in sessionCredentialsList) { | 4034 | for (NSDictionary *theCredentials in sessionCredentialsList) { |
| 4021 | - NSURL *theURL = [theCredentials objectForKey:@"URL"]; | ||
| 4022 | 4035 | ||
| 4023 | - // Port can be nil! | 4036 | + haveFoundExactMatch = NO; |
| 4024 | - if ([[theURL host] isEqualToString:[requestURL host]] && ([theURL port] == [requestURL port] || ([requestURL port] && [[theURL port] isEqualToNumber:[requestURL port]])) && [[theURL scheme] isEqualToString:[requestURL scheme]]) { | 4037 | + NSURL *cachedCredentialsURL = [theCredentials objectForKey:@"URL"]; |
| 4025 | - if (![self responseStatusCode] || (![theCredentials objectForKey:@"AuthenticationRealm"] || [[theCredentials objectForKey:@"AuthenticationRealm"] isEqualToString:[self authenticationRealm]])) { | 4038 | + |
| 4026 | - [sessionCredentialsLock unlock]; | 4039 | + // Find an exact match (same url) |
| 4027 | - return theCredentials; | 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; | ||
| 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; | ||
| 4028 | } | 4065 | } |
| 4029 | } | 4066 | } |
| 4067 | + | ||
| 4068 | + // If we have an exact match for the url, use those credentials | ||
| 4069 | + if (haveFoundExactMatch) { | ||
| 4070 | + [sessionCredentialsLock unlock]; | ||
| 4071 | + return theCredentials; | ||
| 4072 | + } | ||
| 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