Ben Copsey

Start work on generating basic authentication Authorization header (needs tests)

... ... @@ -541,9 +541,17 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
// Returns the maximum amount of data we can read as part of the current measurement period, and sleeps this thread if our allowance is used up
+ (unsigned long)maxUploadReadLength;
#pragma mark miscellany
// Determines whether we're on iPhone OS 2.0 at runtime, currently used to determine whether we should apply a workaround for an issue with converting longs to doubles on iPhone OS 2
+ (BOOL)isiPhoneOS2;
// Used for generating Authorization header when using basic authentication when shouldPresentCredentialsBeforeChallenge is true
// And also by ASIS3Request
+ (NSString *)base64forData:(NSData *)theData;
#pragma mark ===
@property (retain) NSString *username;
@property (retain) NSString *password;
@property (retain) NSString *domain;
... ...
... ... @@ -141,8 +141,6 @@ static BOOL isiPhoneOS2;
@implementation ASIHTTPRequest
#pragma mark init / dealloc
+ (void)initialize
... ... @@ -456,15 +454,31 @@ static BOOL isiPhoneOS2;
}
// If we've already talked to this server and have valid credentials, let's apply them to the request
if ([self shouldPresentCredentialsBeforeChallenge]) {
NSDictionary *credentials = nil;
if ([self useSessionPersistance]) {
NSDictionary *credentials = [self findSessionAuthenticationCredentials];
if (credentials) {
if (!CFHTTPMessageApplyCredentialDictionary(request, (CFHTTPAuthenticationRef)[credentials objectForKey:@"Authentication"], (CFDictionaryRef)[credentials objectForKey:@"Credentials"], NULL)) {
[[self class] removeAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]];
}
credentials = [self findSessionAuthenticationCredentials];
}
// Do we have credentials that might be for basic authentication set?
if ([self username] && [self password] && ![self domain]) {
// If we have stored credentials, is this server asking for basic authentication? If we don't have credentials, we'll assume basic
if (!credentials || (CFStringRef)[credentials objectForKey:@"AuthenticationScheme"] == kCFHTTPAuthenticationSchemeBasic) {
[self addRequestHeader:@"Authorisation" value:[ASIHTTPRequest base64forData:[[NSString stringWithFormat:@"%@:%@",[self username],[self password]] dataUsingEncoding:NSUTF8StringEncoding]]];
}
}
if (credentials && ![[self requestHeaders] objectForKey:@"Authorisation"]) {
// If we've already talked to this server and have valid credentials, let's apply them to the request
if (!CFHTTPMessageApplyCredentialDictionary(request, (CFHTTPAuthenticationRef)[credentials objectForKey:@"Authentication"], (CFDictionaryRef)[credentials objectForKey:@"Credentials"], NULL)) {
[[self class] removeAuthenticationCredentialsFromSessionStore:[credentials objectForKey:@"Credentials"]];
}
}
if ([self useSessionPersistance]) {
credentials = [self findSessionProxyAuthenticationCredentials];
if (credentials) {
if (!CFHTTPMessageApplyCredentialDictionary(request, (CFHTTPAuthenticationRef)[credentials objectForKey:@"Authentication"], (CFDictionaryRef)[credentials objectForKey:@"Credentials"], NULL)) {
... ... @@ -2893,11 +2907,47 @@ static BOOL isiPhoneOS2;
return toRead;
}
#pragma mark miscellany
+ (BOOL)isiPhoneOS2
{
// Value is set in +initialize
return isiPhoneOS2;
}
// From: http://www.cocoadev.com/index.pl?BaseSixtyFour
+ (NSString*)base64forData:(NSData*)theData {
const uint8_t* input = (const uint8_t*)[theData bytes];
NSInteger length = [theData length];
static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
uint8_t* output = (uint8_t*)data.mutableBytes;
for (NSInteger i = 0; i < length; i += 3) {
NSInteger value = 0;
for (NSInteger j = i; j < (i + 3); j++) {
value <<= 8;
if (j < length) {
value |= (0xFF & input[j]);
}
}
NSInteger index = (i / 3) * 4;
output[index + 0] = table[(value >> 18) & 0x3F];
output[index + 1] = table[(value >> 12) & 0x3F];
output[index + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '=';
output[index + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '=';
}
return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
}
#pragma mark ===
@synthesize username;
@synthesize password;
... ...
... ... @@ -20,7 +20,7 @@ static NSString *sharedSecretAccessKey = nil;
@interface ASIS3Request ()
- (void)parseError;
+ (NSData *)HMACSHA1withKey:(NSString *)key forString:(NSString *)string;
+ (NSString *)base64forData:(NSData *)theData;
@property (retain, nonatomic) NSString *currentErrorString;
@end
... ... @@ -138,7 +138,7 @@ static NSString *sharedSecretAccessKey = nil;
} else {
stringToSign = [NSString stringWithFormat:@"%@\n\n\n%@\n%@%@",[self requestMethod],dateString,canonicalizedAmzHeaders,canonicalizedResource];
}
NSString *signature = [ASIS3Request base64forData:[ASIS3Request HMACSHA1withKey:[self secretAccessKey] forString:stringToSign]];
NSString *signature = [ASIHTTPRequest base64forData:[ASIS3Request HMACSHA1withKey:[self secretAccessKey] forString:stringToSign]];
NSString *authorizationString = [NSString stringWithFormat:@"AWS %@:%@",[self accessKey],signature];
[self addRequestHeader:@"Authorization" value:authorizationString];
}
... ... @@ -244,40 +244,6 @@ static NSString *sharedSecretAccessKey = nil;
return [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
}
// From: http://www.cocoadev.com/index.pl?BaseSixtyFour
+ (NSString*)base64forData:(NSData*)theData {
const uint8_t* input = (const uint8_t*)[theData bytes];
NSInteger length = [theData length];
static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
uint8_t* output = (uint8_t*)data.mutableBytes;
for (NSInteger i = 0; i < length; i += 3) {
NSInteger value = 0;
for (NSInteger j = i; j < (i + 3); j++) {
value <<= 8;
if (j < length) {
value |= (0xFF & input[j]);
}
}
NSInteger index = (i / 3) * 4;
output[index + 0] = table[(value >> 18) & 0x3F];
output[index + 1] = table[(value >> 12) & 0x3F];
output[index + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '=';
output[index + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '=';
}
return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
}
@synthesize bucket;
@synthesize path;
@synthesize dateString;
... ...
... ... @@ -557,6 +557,7 @@
- (void)testDigestAuthentication
{
[ASIHTTPRequest removeCredentialsForHost:@"allseeing-i.com" port:0 protocol:@"http" realm:@"Keep out"];
[ASIHTTPRequest clearSession];
NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/digest-authentication"] autorelease];
... ... @@ -564,12 +565,14 @@
BOOL success;
NSError *err;
// Test authentication needed when no credentials supplied
request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request setUseKeychainPersistance:NO];
[request start];
success = [[request error] code] == ASIAuthenticationErrorType;
GHAssertTrue(success,@"Failed to generate permission denied error with no credentials");
// Test wrong credentials supplied
request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request setUseKeychainPersistance:NO];
[request setUsername:@"wrong"];
... ... @@ -578,6 +581,7 @@
success = [[request error] code] == ASIAuthenticationErrorType;
GHAssertTrue(success,@"Failed to generate permission denied error with wrong credentials");
// Test correct credentials supplied
request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request setUseSessionPersistance:YES];
[request setUseKeychainPersistance:YES];
... ... @@ -587,6 +591,7 @@
err = [request error];
GHAssertNil(err,@"Failed to supply correct username and password");
// Ensure credentials are not reused
request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request setUseSessionPersistance:NO];
[request setUseKeychainPersistance:NO];
... ... @@ -594,6 +599,7 @@
success = [[request error] code] == ASIAuthenticationErrorType;
GHAssertTrue(success,@"Reused credentials when we shouldn't have");
// Ensure credentials stored in the session are reused
request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request setUseSessionPersistance:YES];
[request setUseKeychainPersistance:NO];
... ... @@ -603,11 +609,28 @@
[ASIHTTPRequest clearSession];
// Ensure credentials stored in the session were wiped
request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request setUseKeychainPersistance:NO];
[request start];
success = [[request error] code] == ASIAuthenticationErrorType;
GHAssertTrue(success,@"Failed to clear credentials");
// Ensure credentials stored in the keychain are reused
request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request setUseKeychainPersistance:YES];
[request start];
err = [request error];
GHAssertNil(err,@"Failed to reuse credentials");
[ASIHTTPRequest removeCredentialsForHost:@"allseeing-i.com" port:80 protocol:@"http" realm:@"Keep out"];
// Ensure credentials stored in the keychain were wiped
request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request setUseKeychainPersistance:YES];
[request start];
success = [[request error] code] == ASIAuthenticationErrorType;
GHAssertTrue(success,@"Failed to clear credentials");
}
- (void)testNTLMHandshake
... ...