Added canned access policies
Remove GET/ACL constructors Added lots of comments General cleanup
Showing
4 changed files
with
91 additions
and
38 deletions
1 | // | 1 | // |
2 | // ASIS3Request.h | 2 | // ASIS3Request.h |
3 | -// Mac | ||
4 | // | 3 | // |
5 | // Created by Ben Copsey on 30/06/2009. | 4 | // Created by Ben Copsey on 30/06/2009. |
6 | // Copyright 2009 All-Seeing Interactive. All rights reserved. | 5 | // Copyright 2009 All-Seeing Interactive. All rights reserved. |
7 | // | 6 | // |
7 | +// A (basic) class for accessing data stored on Amazon's Simple Storage Service (http://aws.amazon.com/s3/) | ||
8 | +// It uses the REST API, with canned access policies rather than full support for ACLs (though if you build/parse them yourself you can still use ACLs) | ||
8 | 9 | ||
9 | #import <Foundation/Foundation.h> | 10 | #import <Foundation/Foundation.h> |
10 | #import "ASIHTTPRequest.h" | 11 | #import "ASIHTTPRequest.h" |
11 | 12 | ||
13 | +// See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTAccessPolicy.html for what these mean | ||
14 | +extern NSString *const ASIS3AccessPolicyPrivate; // This is the default in S3 when no access policy header is provided | ||
15 | +extern NSString *const ASIS3AccessPolicyPublicRead; | ||
16 | +extern NSString *const ASIS3AccessPolicyPublicReadWrote; | ||
17 | +extern NSString *const ASIS3AccessPolicyAuthenticatedRead; | ||
18 | + | ||
12 | @interface ASIS3Request : ASIHTTPRequest { | 19 | @interface ASIS3Request : ASIHTTPRequest { |
20 | + | ||
21 | + // Your S3 access key. Set it on the request, or set it globally using [ASIS3Request setSharedAccessKey:] | ||
22 | + NSString *accessKey; | ||
23 | + | ||
24 | + // Your S3 secret access key. Set it on the request, or set it globally using [ASIS3Request setSharedSecretAccessKey:] | ||
25 | + NSString *secretAccessKey; | ||
26 | + | ||
27 | + // Name of the bucket to talk to | ||
13 | NSString *bucket; | 28 | NSString *bucket; |
29 | + | ||
30 | + // path to the resource you want to access on S3. Leave empty for the bucket root | ||
14 | NSString *path; | 31 | NSString *path; |
32 | + | ||
33 | + // The string that will be used in the HTTP date header. Generally you'll want to ignore this and let the class add the current date for you, but the accessor is used by the tests | ||
15 | NSString *dateString; | 34 | NSString *dateString; |
35 | + | ||
36 | + // The mime type of the content for PUT requests | ||
37 | + // Set this if having the correct mime type returned to you when you GET the data is important (eg it will be served by a web-server) | ||
38 | + // Will be set to 'application/octet-stream' otherwise in iPhone apps, or autodetected on Mac OS X | ||
16 | NSString *mimeType; | 39 | NSString *mimeType; |
17 | - NSString *accessKey; | 40 | + |
18 | - NSString *secretAccessKey; | 41 | + NSString *accessPolicy; |
19 | } | 42 | } |
20 | 43 | ||
21 | #pragma mark Constructors | 44 | #pragma mark Constructors |
22 | 45 | ||
46 | +// Create a request, building an appropriate url | ||
23 | + (id)requestWithBucket:(NSString *)bucket path:(NSString *)path; | 47 | + (id)requestWithBucket:(NSString *)bucket path:(NSString *)path; |
48 | + | ||
49 | +// Create a PUT request using the file at filePath as the body | ||
24 | + (id)PUTRequestForFile:(NSString *)filePath withBucket:(NSString *)bucket path:(NSString *)path; | 50 | + (id)PUTRequestForFile:(NSString *)filePath withBucket:(NSString *)bucket path:(NSString *)path; |
25 | -+ (id)GETRequestWithBucket:(NSString *)bucket path:(NSString *)path; | ||
26 | -+ (id)listRequestWithBucket:(NSString *)bucket prefix:(NSString *)prefix maxResults:(int)maxResults marker:(NSString *)marker; | ||
27 | -+ (id)ACLRequestWithBucket:(NSString *)bucket path:(NSString *)path; | ||
28 | 51 | ||
52 | +// Create a list request | ||
53 | ++ (id)listRequestWithBucket:(NSString *)bucket prefix:(NSString *)prefix maxResults:(int)maxResults marker:(NSString *)marker; | ||
29 | 54 | ||
55 | +// Generates the request headers S3 needs | ||
56 | +// Automatically called before the request begins in startRequest | ||
30 | - (void)generateS3Headers; | 57 | - (void)generateS3Headers; |
31 | -- (void)setDate:(NSDate *)date; | ||
32 | 58 | ||
59 | +// Uses the supplied date to create a Date header string | ||
60 | +- (void)setDate:(NSDate *)date; | ||
33 | 61 | ||
62 | +// Only works on Mac OS, will always return 'application/octet-stream' on iPhone | ||
34 | + (NSString *)mimeTypeForFileAtPath:(NSString *)path; | 63 | + (NSString *)mimeTypeForFileAtPath:(NSString *)path; |
35 | 64 | ||
36 | #pragma mark Shared access keys | 65 | #pragma mark Shared access keys |
66 | + | ||
67 | +// Get and set the global access key, this will be used for all requests the access key hasn't been set for | ||
37 | + (NSString *)sharedAccessKey; | 68 | + (NSString *)sharedAccessKey; |
38 | + (void)setSharedAccessKey:(NSString *)newAccessKey; | 69 | + (void)setSharedAccessKey:(NSString *)newAccessKey; |
39 | + (NSString *)sharedSecretAccessKey; | 70 | + (NSString *)sharedSecretAccessKey; |
40 | + (void)setSharedSecretAccessKey:(NSString *)newAccessKey; | 71 | + (void)setSharedSecretAccessKey:(NSString *)newAccessKey; |
41 | 72 | ||
42 | -#pragma mark S3 Authentication helpers | ||
43 | -+ (NSData *)HMACSHA1withKey:(NSString *)key forString:(NSString *)string; | ||
44 | -+ (NSString *)base64forData:(NSData *)theData; | ||
45 | 73 | ||
46 | @property (retain) NSString *bucket; | 74 | @property (retain) NSString *bucket; |
47 | @property (retain) NSString *path; | 75 | @property (retain) NSString *path; |
@@ -49,4 +77,5 @@ | @@ -49,4 +77,5 @@ | ||
49 | @property (retain) NSString *mimeType; | 77 | @property (retain) NSString *mimeType; |
50 | @property (retain) NSString *accessKey; | 78 | @property (retain) NSString *accessKey; |
51 | @property (retain) NSString *secretAccessKey; | 79 | @property (retain) NSString *secretAccessKey; |
80 | +@property (assign) NSString *accessPolicy; | ||
52 | @end | 81 | @end |
1 | // | 1 | // |
2 | // ASIS3Request.m | 2 | // ASIS3Request.m |
3 | -// Mac | ||
4 | // | 3 | // |
5 | // Created by Ben Copsey on 30/06/2009. | 4 | // Created by Ben Copsey on 30/06/2009. |
6 | // Copyright 2009 All-Seeing Interactive. All rights reserved. | 5 | // Copyright 2009 All-Seeing Interactive. All rights reserved. |
@@ -9,11 +8,32 @@ | @@ -9,11 +8,32 @@ | ||
9 | #import "ASIS3Request.h" | 8 | #import "ASIS3Request.h" |
10 | #import <CommonCrypto/CommonHMAC.h> | 9 | #import <CommonCrypto/CommonHMAC.h> |
11 | 10 | ||
11 | +NSString* const ASIS3AccessPolicyPrivate = @"private"; | ||
12 | +NSString* const ASIS3AccessPolicyPublicRead = @"public-read"; | ||
13 | +NSString* const ASIS3AccessPolicyPublicReadWrote = @"public-read-write"; | ||
14 | +NSString* const ASIS3AccessPolicyAuthenticatedRead = @"authenticated-read"; | ||
15 | + | ||
12 | static NSString *sharedAccessKey = nil; | 16 | static NSString *sharedAccessKey = nil; |
13 | static NSString *sharedSecretAccessKey = nil; | 17 | static NSString *sharedSecretAccessKey = nil; |
14 | 18 | ||
19 | +// Private stuff | ||
20 | +@interface ASIHTTPRequest () | ||
21 | + + (NSData *)HMACSHA1withKey:(NSString *)key forString:(NSString *)string; | ||
22 | + + (NSString *)base64forData:(NSData *)theData; | ||
23 | +@end | ||
24 | + | ||
15 | @implementation ASIS3Request | 25 | @implementation ASIS3Request |
16 | 26 | ||
27 | +- (void)dealloc | ||
28 | +{ | ||
29 | + [bucket release]; | ||
30 | + [path release]; | ||
31 | + [dateString release]; | ||
32 | + [mimeType release]; | ||
33 | + [accessKey release]; | ||
34 | + [secretAccessKey release]; | ||
35 | + [super dealloc]; | ||
36 | +} | ||
17 | 37 | ||
18 | + (id)requestWithBucket:(NSString *)bucket path:(NSString *)path | 38 | + (id)requestWithBucket:(NSString *)bucket path:(NSString *)path |
19 | { | 39 | { |
@@ -33,25 +53,11 @@ static NSString *sharedSecretAccessKey = nil; | @@ -33,25 +53,11 @@ static NSString *sharedSecretAccessKey = nil; | ||
33 | return request; | 53 | return request; |
34 | } | 54 | } |
35 | 55 | ||
36 | -+ (id)GETRequestWithBucket:(NSString *)bucket path:(NSString *)path | ||
37 | -{ | ||
38 | - ASIS3Request *request = [ASIS3Request requestWithBucket:bucket path:path]; | ||
39 | - [request setRequestMethod:@"GET"]; | ||
40 | - return request; | ||
41 | -} | ||
42 | - | ||
43 | -+ (id)ACLRequestWithBucket:(NSString *)bucket path:(NSString *)path | ||
44 | -{ | ||
45 | - ASIS3Request *request = [ASIS3Request requestWithBucket:bucket path:[NSString stringWithFormat:@"%@?acl",path]]; | ||
46 | - [request setRequestMethod:@"GET"]; | ||
47 | - return request; | ||
48 | -} | ||
49 | 56 | ||
50 | + (id)listRequestWithBucket:(NSString *)bucket prefix:(NSString *)prefix maxResults:(int)maxResults marker:(NSString *)marker | 57 | + (id)listRequestWithBucket:(NSString *)bucket prefix:(NSString *)prefix maxResults:(int)maxResults marker:(NSString *)marker |
51 | { | 58 | { |
52 | ASIS3Request *request = [[[ASIS3Request alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com/?prefix=/%@&max-keys=%hi&marker=%@",bucket,prefix,maxResults,marker]]] autorelease]; | 59 | ASIS3Request *request = [[[ASIS3Request alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com/?prefix=/%@&max-keys=%hi&marker=%@",bucket,prefix,maxResults,marker]]] autorelease]; |
53 | [request setBucket:bucket]; | 60 | [request setBucket:bucket]; |
54 | - [request setRequestMethod:@"GET"]; | ||
55 | return request; | 61 | return request; |
56 | } | 62 | } |
57 | 63 | ||
@@ -60,6 +66,9 @@ static NSString *sharedSecretAccessKey = nil; | @@ -60,6 +66,9 @@ static NSString *sharedSecretAccessKey = nil; | ||
60 | // NSTask does seem to exist in the 2.2.1 SDK, though it's not in the 3.0 SDK. It's probably best if we just use a generic mime type on iPhone all the time. | 66 | // NSTask does seem to exist in the 2.2.1 SDK, though it's not in the 3.0 SDK. It's probably best if we just use a generic mime type on iPhone all the time. |
61 | #if TARGET_OS_IPHONE | 67 | #if TARGET_OS_IPHONE |
62 | return @"application/octet-stream"; | 68 | return @"application/octet-stream"; |
69 | + | ||
70 | +// Grab the mime type using an NSTask to run the 'file' program, with the Mac OS-specific parameters to grab the mime type | ||
71 | +// Perhaps there is a better way to do this? | ||
63 | #else | 72 | #else |
64 | NSTask *task = [[NSTask alloc] init]; | 73 | NSTask *task = [[NSTask alloc] init]; |
65 | [task setLaunchPath: @"/usr/bin/file"]; | 74 | [task setLaunchPath: @"/usr/bin/file"]; |
@@ -86,37 +95,51 @@ static NSString *sharedSecretAccessKey = nil; | @@ -86,37 +95,51 @@ static NSString *sharedSecretAccessKey = nil; | ||
86 | 95 | ||
87 | - (void)generateS3Headers | 96 | - (void)generateS3Headers |
88 | { | 97 | { |
98 | + // If an access key / secret access keyu haven't been set for this request, let's use the shared keys | ||
89 | if (![self accessKey]) { | 99 | if (![self accessKey]) { |
90 | [self setAccessKey:[ASIS3Request sharedAccessKey]]; | 100 | [self setAccessKey:[ASIS3Request sharedAccessKey]]; |
91 | } | 101 | } |
92 | if (![self secretAccessKey]) { | 102 | if (![self secretAccessKey]) { |
93 | [self setAccessKey:[ASIS3Request sharedSecretAccessKey]]; | 103 | [self setAccessKey:[ASIS3Request sharedSecretAccessKey]]; |
94 | } | 104 | } |
105 | + // If a date string hasn't been set, we'll create one from the current time | ||
95 | if (![self dateString]) { | 106 | if (![self dateString]) { |
96 | [self setDate:[NSDate date]]; | 107 | [self setDate:[NSDate date]]; |
97 | } | 108 | } |
109 | + [self addRequestHeader:@"Date" value:[self dateString]]; | ||
110 | + | ||
111 | + // Ensure our formatted string doesn't use '(null)' for the empty path | ||
98 | if (![self path]) { | 112 | if (![self path]) { |
99 | [self setPath:@""]; | 113 | [self setPath:@""]; |
100 | } | 114 | } |
115 | + | ||
101 | 116 | ||
102 | - [self addRequestHeader:@"Date" value:[self dateString]]; | 117 | + NSString *canonicalizedResource = [NSString stringWithFormat:@"/%@/%@",[self bucket],[self path]]; |
103 | 118 | ||
104 | - [self addRequestHeader:@"x-amz-acl" value:@"private"]; | 119 | + // Add a header for the access policy if one was set, otherwise we won't add one (and S3 will default to private) |
105 | - | 120 | + NSString *canonicalizedAmzHeaders = @""; |
121 | + if ([self accessPolicy]) { | ||
122 | + [self addRequestHeader:@"x-amz-acl" value:[self accessPolicy]]; | ||
123 | + canonicalizedAmzHeaders = [NSString stringWithFormat:@"x-amz-acl:%@\n",[self accessPolicy]]; | ||
124 | + } | ||
125 | + | ||
126 | + // Jump through hoops while eating hot food | ||
106 | NSString *stringToSign; | 127 | NSString *stringToSign; |
107 | - NSString *canonicalizedResource = [NSString stringWithFormat:@"/%@/%@",[self bucket],[self path]]; | ||
108 | - NSString *canonicalizedAmzHeaders = @"x-amz-acl:private"; | ||
109 | if ([[self requestMethod] isEqualToString:@"PUT"]) { | 128 | if ([[self requestMethod] isEqualToString:@"PUT"]) { |
110 | [self addRequestHeader:@"Content-Type" value:[self mimeType]]; | 129 | [self addRequestHeader:@"Content-Type" value:[self mimeType]]; |
111 | - stringToSign = [NSString stringWithFormat:@"PUT\n\n%@\n%@\n%@",[self mimeType],dateString,canonicalizedResource]; | 130 | + stringToSign = [NSString stringWithFormat:@"PUT\n\n%@\n%@\n%@%@",[self mimeType],dateString,canonicalizedAmzHeaders,canonicalizedResource]; |
112 | } else { | 131 | } else { |
113 | - stringToSign = [NSString stringWithFormat:@"%@\n\n\n%@\n%@",[self requestMethod],dateString,canonicalizedResource]; | 132 | + stringToSign = [NSString stringWithFormat:@"%@\n\n\n%@\n%@%@",[self requestMethod],dateString,canonicalizedAmzHeaders,canonicalizedResource]; |
114 | } | 133 | } |
115 | - NSLog(@"%@",stringToSign); | ||
116 | NSString *signature = [ASIS3Request base64forData:[ASIS3Request HMACSHA1withKey:[self secretAccessKey] forString:stringToSign]]; | 134 | NSString *signature = [ASIS3Request base64forData:[ASIS3Request HMACSHA1withKey:[self secretAccessKey] forString:stringToSign]]; |
117 | NSString *authorizationString = [NSString stringWithFormat:@"AWS %@:%@",[self accessKey],signature]; | 135 | NSString *authorizationString = [NSString stringWithFormat:@"AWS %@:%@",[self accessKey],signature]; |
118 | [self addRequestHeader:@"Authorization" value:authorizationString]; | 136 | [self addRequestHeader:@"Authorization" value:authorizationString]; |
119 | - | 137 | +} |
138 | + | ||
139 | +- (void)startRequest | ||
140 | +{ | ||
141 | + [self generateS3Headers]; | ||
142 | + [super startRequest]; | ||
120 | } | 143 | } |
121 | 144 | ||
122 | #pragma mark Shared access keys | 145 | #pragma mark Shared access keys |
@@ -203,4 +226,5 @@ static NSString *sharedSecretAccessKey = nil; | @@ -203,4 +226,5 @@ static NSString *sharedSecretAccessKey = nil; | ||
203 | @synthesize mimeType; | 226 | @synthesize mimeType; |
204 | @synthesize accessKey; | 227 | @synthesize accessKey; |
205 | @synthesize secretAccessKey; | 228 | @synthesize secretAccessKey; |
229 | +@synthesize accessPolicy; | ||
206 | @end | 230 | @end |
1 | // | 1 | // |
2 | // ASIS3RequestTests.h | 2 | // ASIS3RequestTests.h |
3 | -// Mac | 3 | +// asi-http-request |
4 | // | 4 | // |
5 | // Created by Ben Copsey on 12/07/2009. | 5 | // Created by Ben Copsey on 12/07/2009. |
6 | // Copyright 2009 All-Seeing Interactive. All rights reserved. | 6 | // Copyright 2009 All-Seeing Interactive. All rights reserved. |
1 | // | 1 | // |
2 | // ASIS3RequestTests.m | 2 | // ASIS3RequestTests.m |
3 | -// Mac | 3 | +// asi-http-request |
4 | // | 4 | // |
5 | // Created by Ben Copsey on 12/07/2009. | 5 | // Created by Ben Copsey on 12/07/2009. |
6 | // Copyright 2009 All-Seeing Interactive. All rights reserved. | 6 | // Copyright 2009 All-Seeing Interactive. All rights reserved. |
@@ -57,9 +57,9 @@ | @@ -57,9 +57,9 @@ | ||
57 | GHAssertTrue(success,@"Failed to generate the correct authorisation header for a list request"); | 57 | GHAssertTrue(success,@"Failed to generate the correct authorisation header for a list request"); |
58 | 58 | ||
59 | // Test fetch ACL | 59 | // Test fetch ACL |
60 | - path = @""; | 60 | + path = @"?acl"; |
61 | dateString = @"Tue, 27 Mar 2007 19:44:46 +0000"; | 61 | dateString = @"Tue, 27 Mar 2007 19:44:46 +0000"; |
62 | - request = [ASIS3Request ACLRequestWithBucket:bucket path:path]; | 62 | + request = [ASIS3Request requestWithBucket:bucket path:path]; |
63 | [request setDateString:dateString]; | 63 | [request setDateString:dateString]; |
64 | [request setSecretAccessKey:secretAccessKey]; | 64 | [request setSecretAccessKey:secretAccessKey]; |
65 | [request setAccessKey:accessKey]; | 65 | [request setAccessKey:accessKey]; |
-
Please register or login to post a comment