Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added Token Based Reconnection and tested everything #758

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// XMPPStream+XMPPTBRAuthentication.h
// XMPPFramework
//
// Created by Andres Canal on 7/6/16.
// Copyright © 2016 Inaka. All rights reserved.
//

#import "XMPPFramework.h"
#import "XMPPStream.h"

@interface XMPPStream (XMPPTBRAuthentication)

- (BOOL)authenticateWithTBR:(NSString *)authToken error:(NSError **)errPtr;
- (BOOL)supportsTBRAuthentication;

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// XMPPStream+XMPPTBRAuthentication.m
// XMPPFramework
//
// Created by Andres Canal on 7/6/16.
// Copyright © 2016 Inaka. All rights reserved.
//

#import "XMPPStream+XMPPTBRAuthentication.h"
#import "XMPPTBRAuthentication.h"
#import "XMPPInternal.h"

@implementation XMPPStream (TBRAuthentication)

- (BOOL)supportsTBRAuthentication{
return [self supportsAuthenticationMechanism:[XMPPTBRAuthentication mechanismName]];
}

- (BOOL)authenticateWithTBR:(nonnull NSString *)authToken error:(NSError **)errPtr {

__block BOOL result = YES;
__block NSError *err = nil;

dispatch_block_t block = ^{ @autoreleasepool {

if ([self supportsTBRAuthentication]) {

XMPPTBRAuthentication *tbrAuthentication = [[XMPPTBRAuthentication alloc] initWithStream:self
token:authToken];

result = [self authenticate:tbrAuthentication error:&err];
} else {
NSString *errMsg = @"The server does not support Token-based reconnection.";
NSDictionary *info = @{NSLocalizedDescriptionKey : errMsg};

err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamUnsupportedAction userInfo:info];

result = NO;
}
}};

if (dispatch_get_specific(self.xmppQueueTag))
block();
else
dispatch_sync(self.xmppQueue, block);

if (errPtr)
*errPtr = err;

return result;
}

@end
17 changes: 17 additions & 0 deletions Authentication/Token-Based-Reconnection/XMPPTBRAuthentication.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// XMPPTBRAuthentication.h
// XMPPFramework
//
// Created by Andres Canal on 7/6/16.
// Copyright © 2016 Inaka. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "XMPPSASLAuthentication.h"
#import "XMPPStream.h"

@interface XMPPTBRAuthentication : NSObject <XMPPSASLAuthentication>

- (nonnull instancetype)initWithStream:(nonnull XMPPStream *)stream token:(nonnull NSString *)aToken;

@end
71 changes: 71 additions & 0 deletions Authentication/Token-Based-Reconnection/XMPPTBRAuthentication.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// XMPPTBRAuthentication.m
// XMPPFramework
//
// Created by Andres Canal on 7/6/16.
// Copyright © 2016 Inaka. All rights reserved.
//

#import "XMPPTBRAuthentication.h"
#import "XMPPInternal.h"
#import "NSXMLElement+XMPP.h"

@implementation XMPPTBRAuthentication {
#if __has_feature(objc_arc_weak)
__weak XMPPStream *xmppStream;
#else
__unsafe_unretained XMPPStream *xmppStream;
#endif
NSString *authToken;
}

+ (NSString *)mechanismName {
return @"X-OAUTH";
}

- (id)initWithStream:(nonnull XMPPStream *)stream password:(nonnull NSString *)password {
return [super init];
}

- (id)initWithStream:(nonnull XMPPStream *)stream token:(nonnull NSString *)aToken {
if( self = [super init]) {
xmppStream = stream;
authToken = aToken;
}

return self;
}

- (BOOL)start:(NSError **)errPtr {

if(!authToken) {
NSString *errMsg = @"Missing auth token.";
NSDictionary *info = @{NSLocalizedDescriptionKey : errMsg};

NSError *err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidParameter userInfo:info];

if (errPtr) *errPtr = err;
return NO;
}

// <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="X-OAUTH">auth_token</auth>

NSXMLElement *auth = [NSXMLElement elementWithName:@"auth" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
[auth addAttributeWithName:@"mechanism" stringValue:@"X-OAUTH"];
auth.stringValue = authToken;

[xmppStream sendAuthElement:auth];

return true;
}

- (XMPPHandleAuthResponse)handleAuth:(NSXMLElement *)auth {

if ([[auth name] isEqualToString:@"success"]) {
return XMPP_AUTH_SUCCESS;
}

return XMPP_AUTH_FAIL;
}

@end
21 changes: 21 additions & 0 deletions Authentication/Token-Based-Reconnection/XMPPTBReconnection.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// XMPPTBReconnection.h
// XMPPFramework
//
// Created by Andres Canal on 7/5/16.
// Copyright © 2016 Inaka. All rights reserved.
//

#import "XMPPFramework.h"
#import "XMPPIDTracker.h"

@interface XMPPTBReconnection : XMPPModule {
XMPPIDTracker *responseTracker;
}
- (void) getAuthToken;
@end

@protocol XMPPTBReconnectionDelegate
- (void)xmppTBReconnection:(nonnull XMPPTBReconnection *)sender didReceiveToken:(nonnull NSDictionary<NSString *, NSString *> *) token;
- (void)xmppTBReconnection:(nonnull XMPPTBReconnection *)sender didFailToReceiveToken:(nonnull XMPPIQ *)iq;
@end
81 changes: 81 additions & 0 deletions Authentication/Token-Based-Reconnection/XMPPTBReconnection.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// XMPPTBReconnection.m
// XMPPFramework
//
// Created by Andres Canal on 7/5/16.
// Copyright © 2016 Inaka. All rights reserved.
//

#import "XMPPTBReconnection.h"
#import "XMPPFramework.h"
#import "XMPPIQ.h"

@implementation XMPPTBReconnection

- (void) getAuthToken {
// <iq from='crone1@shakespeare.lit/desktop'
// id='create1'
// to='coven@muclight.shakespeare.lit'
// type='get'>
// <query xmlns='erlang-solutions.com:xmpp:token-auth:0'/>
// </iq>

dispatch_block_t block = ^{ @autoreleasepool {

NSString *iqID = [XMPPStream generateUUID];
NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"];
[iq addAttributeWithName:@"id" stringValue:iqID];
[iq addAttributeWithName:@"to" stringValue:self.xmppStream.myJID.full];
[iq addAttributeWithName:@"type" stringValue:@"get"];

NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"erlang-solutions.com:xmpp:token-auth:0"];
[iq addChild:query];

[responseTracker addID:iqID
target:self
selector:@selector(handleGetAuthToken:withInfo:)
timeout:60.0];

[xmppStream sendElement:iq];
}};

if (dispatch_get_specific(moduleQueueTag))
block();
else
dispatch_async(moduleQueue, block);
}

- (void)handleGetAuthToken:(XMPPIQ *)iq withInfo:(id <XMPPTrackingInfo>)info {
if ([[iq type] isEqualToString:@"result"]){
NSXMLElement *items = [iq elementForName:@"items"];

NSMutableDictionary *tokensDictionary = [[NSMutableDictionary alloc] init];
for (NSXMLElement *element in items.children) {
tokensDictionary[element.name] = element.stringValue;
}
[multicastDelegate xmppTBReconnection:self didReceiveToken:tokensDictionary];
}else{
[multicastDelegate xmppTBReconnection:self didFailToReceiveToken:iq];
}
}

- (BOOL)activate:(XMPPStream *)aXmppStream {
if ([super activate:aXmppStream]){
responseTracker = [[XMPPIDTracker alloc] initWithDispatchQueue:moduleQueue];

return YES;
}
return NO;
}

- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq {
NSString *type = [iq type];
if ([type isEqualToString:@"result"] || [type isEqualToString:@"error"]){
return [responseTracker invokeForID:[iq elementID] withObject:iq];
}

return NO;
}


@end
2 changes: 1 addition & 1 deletion Xcode/Testing-Shared/XMPPMockStream.h
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
- (void)fakeIQResponse:(XMPPIQ *) iq;
- (void)fakeMessageResponse:(XMPPMessage *) message;

@property BOOL supportsTBR;
@property (nonatomic, copy) void (^elementReceived)(XMPPElement *element);

@end
11 changes: 11 additions & 0 deletions Xcode/Testing-Shared/XMPPMockStream.m
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ - (BOOL) isAuthenticated {
return YES;
}

- (BOOL)supportsTBRAuthentication{
return self.supportsTBR;
}

- (void)fakeMessageResponse:(XMPPMessage *) message {
[self injectElement:message];
}
Expand All @@ -37,4 +41,11 @@ - (void)sendElement:(XMPPElement *)element {
}
}

- (void)sendAuthElement:(XMPPElement *)element {
[super sendAuthElement:element];
if(self.elementReceived) {
self.elementReceived(element);
}
}

@end
77 changes: 77 additions & 0 deletions Xcode/Testing-Shared/XMPPTBRAuthenticationTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//
// XMPPTBRAuthenticationTests.m
// XMPPFrameworkTests
//
// Created by Andres Canal on 7/6/16.
//
//

#import <XCTest/XCTest.h>
#import "XMPPMockStream.h"
#import "XMPPTBReconnection.h"
#import "XMPPTBRAuthentication.h"
#import "XMPPJID.h"
#import "XMPPStream+XMPPTBRAuthentication.h"

@interface XMPPTBRAuthenticationTests : XCTestCase <XMPPStreamDelegate>
@property (nonatomic, strong) XCTestExpectation *delegateExpectation;
@end

@implementation XMPPTBRAuthenticationTests

- (void)testTBRNotSupported {
NSError *error;

XMPPMockStream *streamTest = [[XMPPMockStream alloc] init];
streamTest.supportsTBR = NO;
[streamTest authenticateWithTBR:@"token-token-token" error:&error];

XCTAssertEqualObjects(error.domain, XMPPStreamErrorDomain);
XCTAssertEqual(error.code, XMPPStreamUnsupportedAction);
XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], @"The server does not support Token-based reconnection.");
}

- (void)testSuccessTBR {
self.delegateExpectation = [self expectationWithDescription:@"TBR expectation"];

XMPPMockStream *streamTest = [[XMPPMockStream alloc] init];
streamTest.supportsTBR = YES;
streamTest.myJID = [XMPPJID jidWithString:@"andres@test.com"];

streamTest.elementReceived = ^void(NSXMLElement *element) {
XCTAssertEqualObjects(element.name, @"auth");
XCTAssertEqualObjects([element attributeForName:@"mechanism"].stringValue, @"X-OAUTH");
XCTAssertEqualObjects(element.stringValue, @"token-token-token");

[self.delegateExpectation fulfill];
};

[streamTest authenticateWithTBR:@"token-token-token" error:nil];

[self waitForExpectationsWithTimeout:5 handler:^(NSError * _Nullable error) {
if(error){
XCTFail(@"Expectation Failed with error: %@", error);
}
}];
}

- (void)testFailureHandleAuth {
XMPPMockStream *streamTest = [[XMPPMockStream alloc] init];
XMPPTBRAuthentication *auth = [[XMPPTBRAuthentication alloc] initWithStream:streamTest token:@"test"];

NSXMLElement *failureElement = [NSXMLElement elementWithName:@"failure" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
NSXMLElement *notAuthorized = [NSXMLElement elementWithName:@"not-authorized"];
[failureElement addChild:notAuthorized];

XCTAssertEqual([auth handleAuth:failureElement], XMPP_AUTH_FAIL);
}

- (void)testSuccessHandleAuth {
XMPPMockStream *streamTest = [[XMPPMockStream alloc] init];
XMPPTBRAuthentication *auth = [[XMPPTBRAuthentication alloc] initWithStream:streamTest token:@"test"];

NSXMLElement *successElement = [NSXMLElement elementWithName:@"success" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
XCTAssertEqual([auth handleAuth:successElement], XMPP_AUTH_SUCCESS);
}

@end
Loading