Skip to content
Browse files

BrowserID support for replicator (untested)

To use it with non persistent replications, set a TDBrowserIDAuthorizer.

With persistent replications, add a "auth" sub-object of the "source"
or "target" object (just as for OAuth) and in it put a "browserid"
object with an "assertion" property whose value is the BrowserID
assertion.

This obviously requires that the remote server have BrowserID support.
There is a CouchDB-BrowserID plugin, and IrisCouch's servers have it.
  • Loading branch information...
1 parent efb7fb5 commit 649d14f71b51338e761784b6af6d165f75c556fb @snej snej committed
View
5 Source/TDAuthorizer.h
@@ -20,6 +20,11 @@
- (NSString*) authorizeHTTPMessage: (CFHTTPMessageRef)message
forRealm: (NSString*)realm;
+@optional
+
+@property (readonly) NSDictionary* loginParameters;
+@property (readonly) NSString* loginPath;
+
@end
View
19 Source/TDAuthorizer.m
@@ -72,6 +72,25 @@ - (NSString*) description {
return $sprintf(@"%@[%@/****]", self.class, _credential.user);
}
+#if 0
+// If enabled, these methods would make TouchDB use cookie-based login intstead of basic auth;
+// but there's not really much point in doing so, as such logins expire, which would cause trouble
+// with long-lived replications.
+
+- (NSString*) loginPath {
+ return @"/_session";
+}
+
+- (NSDictionary*) loginParameters {
+ NSString* username = _credential.user;
+ NSString* password = _credential.password;
+ if (username && password) {
+ return @{@"name": username, @"password": password};
+ }
+ return nil;
+}
+#endif
+
@end
View
17 Source/TDBrowserIDAuthorizer.h
@@ -0,0 +1,17 @@
+//
+// TDBrowserIDAuthorizer.h
+// TouchDB
+//
+// Created by Jens Alfke on 1/9/13.
+//
+//
+
+#import "TDAuthorizer.h"
+
+@interface TDBrowserIDAuthorizer: NSObject <TDAuthorizer>
+
+- (id) initWithAssertion: (NSString*)assertion;
+
+@property (readonly) NSString* assertion;
+
+@end
View
47 Source/TDBrowserIDAuthorizer.m
@@ -0,0 +1,47 @@
+//
+// TDBrowserIDAuthorizer.m
+// TouchDB
+//
+// Created by Jens Alfke on 1/9/13.
+//
+//
+
+#import "TDBrowserIDAuthorizer.h"
+
+@implementation TDBrowserIDAuthorizer
+
+- (id) initWithAssertion:(NSString *)assertion {
+ self = [super init];
+ if (self) {
+ if (!_assertion)
+ return nil;
+ _assertion = [assertion copy];
+ }
+ return self;
+}
+
+@synthesize assertion=_assertion;
+
+- (NSString*) authorizeURLRequest: (NSMutableURLRequest*)request
+ forRealm: (NSString*)realm
+{
+ // Auth is via cookie, which is automatically added by CFNetwork.
+ return nil;
+}
+
+- (NSString*) authorizeHTTPMessage: (CFHTTPMessageRef)message
+ forRealm: (NSString*)realm
+{
+ // Auth is via cookie, which is automatically added by CFNetwork.
+ return nil;
+}
+
+- (NSString*) loginPath {
+ return @"/_browserid";
+}
+
+- (NSDictionary*) loginParameters {
+ return @{@"assertion": _assertion};
+}
+
+@end
View
2 Source/TDPuller.m
@@ -407,7 +407,7 @@ - (void) pullBulkRevisions: (NSArray*)bulkRevs {
NSMutableArray* remainingRevs = [bulkRevs mutableCopy];
NSArray* keys = [bulkRevs my_map: ^(TD_Revision* rev) { return rev.docID; }];
[self sendAsyncRequest: @"POST"
- path: @"/_all_docs?include_docs=true"
+ path: @"_all_docs?include_docs=true"
body: $dict({@"keys", keys})
onCompletion:^(id result, NSError *error) {
if (error) {
View
6 Source/TDPusher.m
@@ -188,7 +188,7 @@ - (void) processInbox: (TD_RevisionList*)changes {
// Call _revs_diff on the target db:
[self asyncTaskStarted];
- [self sendAsyncRequest: @"POST" path: @"/_revs_diff" body: diffs
+ [self sendAsyncRequest: @"POST" path: @"_revs_diff" body: diffs
onCompletion:^(NSDictionary* results, NSError* error) {
if (error) {
self.error = error;
@@ -264,7 +264,7 @@ - (void) uploadBulkDocs: (NSArray*)docsToSend
self.changesTotal += numDocsToSend;
[self asyncTaskStarted];
[self sendAsyncRequest: @"POST"
- path: @"/_bulk_docs"
+ path: @"_bulk_docs"
body: $dict({@"docs", docsToSend},
{@"new_edits", $false})
onCompletion: ^(NSDictionary* response, NSError *error) {
@@ -384,7 +384,7 @@ - (void) uploadJSONRevision: (TD_Revision*)rev {
}
[self asyncTaskStarted];
- NSString* path = $sprintf(@"/%@?new_edits=false", TDEscapeID(rev.docID));
+ NSString* path = $sprintf(@"%@?new_edits=false", TDEscapeID(rev.docID));
[self sendAsyncRequest: @"PUT"
path: path
body: rev.properties
View
2 Source/TDRemoteRequest.m
@@ -319,7 +319,7 @@ - (NSURLRequest *)connection:(NSURLConnection *)connection
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
- LogTo(RemoteRequest, @"%@: Got %lu bytes", self, (unsigned long)data.length);
+ LogTo(RemoteRequestVerbose, @"%@: Got %lu bytes", self, (unsigned long)data.length);
}
View
41 Source/TDReplicator.m
@@ -338,7 +338,7 @@ - (BOOL) goOnline {
_lastSequence = nil;
self.error = nil;
- [self fetchRemoteCheckpointDoc];
+ [self login];
[self postProgressChanged];
}
return YES;
@@ -430,6 +430,33 @@ - (void) revisionFailed {
}
+- (void) login {
+ if ([_authorizer respondsToSelector: @selector(loginParameters)]) {
+ NSDictionary* loginParameters = _authorizer.loginParameters;
+ if (loginParameters != nil) {
+ LogTo(Sync, @"Logging in with %@ at %@ ...", _authorizer.class, _authorizer.loginPath);
+ [self asyncTaskStarted];
+ [self sendAsyncRequest: @"POST"
+ path: _authorizer.loginPath
+ body: _authorizer.loginParameters
+ onCompletion: ^(id result, NSError *error) {
+ if (error) {
+ LogTo(Sync, @"Login failed!");
+ self.error = error;
+ } else {
+ LogTo(Sync, @"Successfully logged in!");
+ [self fetchRemoteCheckpointDoc];
+ }
+ [self asyncTasksFinished: 1];
+ }];
+ return;
+ }
+ }
+
+ [self fetchRemoteCheckpointDoc];
+}
+
+
#pragma mark - HTTP REQUESTS:
@@ -439,8 +466,12 @@ - (TDRemoteJSONRequest*) sendAsyncRequest: (NSString*)method
onCompletion: (TDRemoteRequestCompletionBlock)onCompletion
{
LogTo(SyncVerbose, @"%@: %@ .%@", self, method, relativePath);
- NSString* urlStr = [_remote.absoluteString stringByAppendingString: relativePath];
- NSURL* url = [NSURL URLWithString: urlStr];
+ NSURL* url;
+ if ([relativePath hasPrefix: @"/"]) {
+ url = [[NSURL URLWithString: relativePath relativeToURL: _remote] absoluteURL];
+ } else {
+ url = [_remote URLByAppendingPathComponent: relativePath];
+ }
onCompletion = [onCompletion copy];
// under ARC, using variable req used directly inside the block results in a compiler error (it could have undefined value).
@@ -522,7 +553,7 @@ - (void) fetchRemoteCheckpointDoc {
[self asyncTaskStarted];
TDRemoteJSONRequest* request =
[self sendAsyncRequest: @"GET"
- path: [@"/_local/" stringByAppendingString: checkpointID]
+ path: [@"_local/" stringByAppendingString: checkpointID]
body: nil
onCompletion: ^(id response, NSError* error) {
// Got the response:
@@ -577,7 +608,7 @@ - (void) saveLastSequence {
_savingCheckpoint = YES;
NSString* checkpointID = self.remoteCheckpointDocID;
[self sendAsyncRequest: @"PUT"
- path: [@"/_local/" stringByAppendingString: checkpointID]
+ path: [@"_local/" stringByAppendingString: checkpointID]
body: body
onCompletion: ^(id response, NSError* error) {
_savingCheckpoint = NO;
View
33 Source/TDReplicatorManager.m
@@ -25,6 +25,7 @@
#import "TDPuller.h"
#import "TD_View.h"
#import "TDOAuth1Authorizer.h"
+#import "TDBrowserIDAuthorizer.h"
#import "TDInternal.h"
#import "TDMisc.h"
#import "MYBlockUtils.h"
@@ -168,18 +169,26 @@ - (TDStatus) parseReplicatorProperties: (NSDictionary*)properties
if (outAuthorizer) {
*outAuthorizer = nil;
NSDictionary* auth = $castIf(NSDictionary, remoteDict[@"auth"]);
- NSDictionary* oauth = $castIf(NSDictionary, auth[@"oauth"]);
- if (oauth) {
- NSString* consumerKey = $castIf(NSString, oauth[@"consumer_key"]);
- NSString* consumerSec = $castIf(NSString, oauth[@"consumer_secret"]);
- NSString* token = $castIf(NSString, oauth[@"token"]);
- NSString* tokenSec = $castIf(NSString, oauth[@"token_secret"]);
- NSString* sigMethod = $castIf(NSString, oauth[@"signature_method"]);
- *outAuthorizer = [[TDOAuth1Authorizer alloc] initWithConsumerKey: consumerKey
- consumerSecret: consumerSec
- token: token
- tokenSecret: tokenSec
- signatureMethod: sigMethod];
+ if (auth) {
+ NSDictionary* oauth = $castIf(NSDictionary, auth[@"oauth"]);
+ if (oauth) {
+ NSString* consumerKey = $castIf(NSString, oauth[@"consumer_key"]);
+ NSString* consumerSec = $castIf(NSString, oauth[@"consumer_secret"]);
+ NSString* token = $castIf(NSString, oauth[@"token"]);
+ NSString* tokenSec = $castIf(NSString, oauth[@"token_secret"]);
+ NSString* sigMethod = $castIf(NSString, oauth[@"signature_method"]);
+ *outAuthorizer = [[TDOAuth1Authorizer alloc] initWithConsumerKey: consumerKey
+ consumerSecret: consumerSec
+ token: token
+ tokenSecret: tokenSec
+ signatureMethod: sigMethod];
+ } else {
+ NSDictionary* browserid = $castIf(NSDictionary, auth[@"browserid"]);
+ if (browserid) {
+ NSString* assertion = $castIf(NSString, browserid[@"assertion"]);
+ *outAuthorizer = [[TDBrowserIDAuthorizer alloc] initWithAssertion: assertion];
+ }
+ }
if (!*outAuthorizer)
return kTDStatusBadRequest;
}
View
10 TouchDB.xcodeproj/project.pbxproj
@@ -166,6 +166,9 @@
2773ADC714BD1EB80027A292 /* TD_Database+LocalDocs.h in Headers */ = {isa = PBXBuildFile; fileRef = 2773ADC514BD1EB80027A292 /* TD_Database+LocalDocs.h */; settings = {ATTRIBUTES = (Public, ); }; };
2773ADC814BD1EB80027A292 /* TD_Database+LocalDocs.m in Sources */ = {isa = PBXBuildFile; fileRef = 2773ADC614BD1EB80027A292 /* TD_Database+LocalDocs.m */; };
2773ADC914BD1EB80027A292 /* TD_Database+LocalDocs.m in Sources */ = {isa = PBXBuildFile; fileRef = 2773ADC614BD1EB80027A292 /* TD_Database+LocalDocs.m */; };
+ 2776A538169F4C41006FF199 /* TDBrowserIDAuthorizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 2776A536169F4C41006FF199 /* TDBrowserIDAuthorizer.h */; };
+ 2776A539169F4C41006FF199 /* TDBrowserIDAuthorizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 2776A537169F4C41006FF199 /* TDBrowserIDAuthorizer.m */; };
+ 2776A53A169F4C41006FF199 /* TDBrowserIDAuthorizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 2776A537169F4C41006FF199 /* TDBrowserIDAuthorizer.m */; };
2781E3C915C31FDA00E970DC /* MYRegexUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 2781E3C715C31FDA00E970DC /* MYRegexUtils.h */; };
2781E3CA15C31FDA00E970DC /* MYRegexUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 2781E3C815C31FDA00E970DC /* MYRegexUtils.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
2781E3CB15C31FDA00E970DC /* MYRegexUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 2781E3C815C31FDA00E970DC /* MYRegexUtils.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
@@ -644,6 +647,8 @@
27731F40149685A100815D67 /* list_area___checkbox___unchecked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "list_area___checkbox___unchecked.png"; sourceTree = "<group>"; };
2773ADC514BD1EB80027A292 /* TD_Database+LocalDocs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TD_Database+LocalDocs.h"; sourceTree = "<group>"; };
2773ADC614BD1EB80027A292 /* TD_Database+LocalDocs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "TD_Database+LocalDocs.m"; sourceTree = "<group>"; };
+ 2776A536169F4C41006FF199 /* TDBrowserIDAuthorizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TDBrowserIDAuthorizer.h; sourceTree = "<group>"; };
+ 2776A537169F4C41006FF199 /* TDBrowserIDAuthorizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TDBrowserIDAuthorizer.m; sourceTree = "<group>"; };
2781E3C715C31FDA00E970DC /* MYRegexUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYRegexUtils.h; sourceTree = "<group>"; };
2781E3C815C31FDA00E970DC /* MYRegexUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYRegexUtils.m; sourceTree = "<group>"; };
27821BB5148E7D6F0099B373 /* TDReplicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TDReplicator.h; sourceTree = "<group>"; };
@@ -1223,6 +1228,8 @@
270F5704156AE0BF000FEB8F /* TDAuthorizer.m */,
27F128AF156AC1C8008465C2 /* TDOAuth1Authorizer.h */,
27F128B0156AC1C9008465C2 /* TDOAuth1Authorizer.m */,
+ 2776A536169F4C41006FF199 /* TDBrowserIDAuthorizer.h */,
+ 2776A537169F4C41006FF199 /* TDBrowserIDAuthorizer.m */,
279C7E2C14F424090004A1E8 /* TDSequenceMap.h */,
279C7E2D14F424090004A1E8 /* TDSequenceMap.m */,
27A073EA14C0BB6200F52FE7 /* TDMisc.h */,
@@ -1522,6 +1529,7 @@
275F535015C04F7B00BAF578 /* MYStreamUtils.h in Headers */,
2781E3C915C31FDA00E970DC /* MYRegexUtils.h in Headers */,
27ED9AA1163B01D5000C844A /* TDSocketChangeTracker.h in Headers */,
+ 2776A538169F4C41006FF199 /* TDBrowserIDAuthorizer.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2070,6 +2078,7 @@
27F128B8156AC8EC008465C2 /* OAConsumer.m in Sources */,
27F128B9156AC8F0008465C2 /* OAMutableURLRequest.m in Sources */,
270F5702156AD215000FEB8F /* OARequestParameter.m in Sources */,
+ 2776A539169F4C41006FF199 /* TDBrowserIDAuthorizer.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2188,6 +2197,7 @@
27F128A9156ABFE0008465C2 /* NSMutableURLRequest+Parameters.m in Sources */,
275F535215C04F7B00BAF578 /* MYStreamUtils.m in Sources */,
2781E3CB15C31FDA00E970DC /* MYRegexUtils.m in Sources */,
+ 2776A53A169F4C41006FF199 /* TDBrowserIDAuthorizer.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

0 comments on commit 649d14f

Please sign in to comment.
Something went wrong with that request. Please try again.