Private
Public Access
1
0
Files
Kordophone/kordophone/Bridge/MBIMHTTPConnection.m

187 lines
5.5 KiB
Mathematica
Raw Normal View History

//
// MBIMHTTPConnection.m
// CocoaHTTPServer
//
// Created by James Magahern on 11/16/18.
// Copyright © 2018 James Magahern. All rights reserved.
//
#import "MBIMHTTPConnection.h"
2018-11-16 01:40:02 -08:00
#import "MBIMBridge.h"
2019-01-22 23:31:36 -08:00
#import "MBIMBridge_Private.h"
#import "MBIMBridgeOperation.h"
#import "MBIMAuthToken.h"
#import "MBIMUpdateQueue.h"
#import "MBIMURLUtilities.h"
2019-01-22 23:31:36 -08:00
#import <Security/Security.h>
#import "HTTPMessage.h"
#import "GCDAsyncSocket.h"
@interface HTTPConnection (/* INTERNAL */)
- (BOOL)isAuthenticated;
@end
2019-01-22 23:31:36 -08:00
@implementation MBIMHTTPConnection {
NSMutableData *_bodyData;
2018-11-17 01:07:55 -08:00
MBIMBridgeOperation *_currentOperation;
}
2019-01-22 23:31:36 -08:00
- (BOOL)isSecureServer
{
return [[MBIMBridge sharedInstance] usesSSL];
}
- (NSArray *)sslIdentityAndCertificates
{
return [[MBIMBridge sharedInstance] sslCertificateAndIdentity];
}
2019-01-23 20:26:35 -08:00
- (BOOL)isPasswordProtected:(NSString *)path
{
if ([[MBIMBridge sharedInstance] usesAccessControl]) {
NSURL *url = [NSURL URLWithString:path];
NSString *endpointName = [url lastPathComponent];
Class operationClass = [MBIMBridgeOperation operationClassForEndpointName:endpointName];
return [operationClass requiresAuthentication];
}
return NO;
2019-01-23 20:26:35 -08:00
}
- (NSString *)passwordForUser:(NSString *)username
{
MBIMBridge *bridge = [MBIMBridge sharedInstance];
if ([username isEqualToString:bridge.authUsername]) {
return bridge.authPassword;
}
// nil means "user not in system"
return nil;
}
- (BOOL)isAuthenticated
{
NSString *authInfo = [request headerField:@"Authorization"];
if ([authInfo hasPrefix:@"Bearer"]) {
NSArray *bearerAuthTuple = [authInfo componentsSeparatedByString:@" "];
if ([bearerAuthTuple count] == 2) {
NSString *jwtToken = [bearerAuthTuple objectAtIndex:1];
MBIMAuthToken *authToken = [[MBIMAuthToken alloc] initWithTokenString:jwtToken];
return [authToken isValid];
}
}
return [super isAuthenticated];
2019-01-23 20:26:35 -08:00
}
- (BOOL)useDigestAccessAuthentication
{
// TODO: should use digest all the time, but it's a bit more complicated. Allow this to be NO if
// SSL is on, because then at least basic auth is encrypted
return ![[MBIMBridge sharedInstance] usesSSL];
}
- (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path
{
if ([method isEqualToString:@"GET"] || [method isEqualToString:@"POST"]) {
return YES;
}
return NO;
}
- (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path
{
__block NSObject<HTTPResponse> *response = nil;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
MBIMBridgeOperationCompletionBlock completion = ^(NSObject<HTTPResponse> *incomingResponse) {
response = incomingResponse;
dispatch_semaphore_signal(sema);
};
NSURL *url = [NSURL URLWithString:path];
NSString *endpointName = [url lastPathComponent];
BOOL requestTimedOut = NO;
Class operationClass = [MBIMBridgeOperation operationClassForEndpointName:endpointName];
if (operationClass != nil) {
2018-11-17 01:07:55 -08:00
_currentOperation = [[operationClass alloc] initWithRequestURL:url completion:completion];
_currentOperation.requestBodyData = _bodyData;
_currentOperation.request = self->request;
2018-11-17 01:07:55 -08:00
[[[MBIMBridge sharedInstance] operationQueue] addOperation:_currentOperation];
long status = dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(60.0 * NSEC_PER_SEC)));
if (status != 0) {
requestTimedOut = YES;
}
} else {
response = [[HTTPErrorResponse alloc] initWithErrorCode:404];
}
if (requestTimedOut) {
response = [_currentOperation cancelAndReturnTimeoutResponse];
}
return response;
}
- (WebSocket *)webSocketForURI:(NSString *)path
{
NSURL *url = [NSURL URLWithString:path];
NSString *endpointName = [url lastPathComponent];
NSString *authTokenString = [url valueForQueryItemWithName:@"token"];
MBIMAuthToken *queryAuthToken = [[MBIMAuthToken alloc] initWithTokenString:authTokenString];
NSLog(@"Websocket for URI: %@ | authenticated request: %@", path, [self isAuthenticated] ? @"YES" : @"NO");
if ([endpointName isEqualToString:@"updates"]) {
if (![self isAuthenticated] && ![queryAuthToken isValid]) {
NSLog(@"Websocket: auth invalid, rejecting.");
NSLog(@"Query Token: %@, raw: %@", queryAuthToken, authTokenString);
// Respond with 401 unauthorized
HTTPMessage *response = [[HTTPMessage alloc] initResponseWithStatusCode:401 description:nil version:HTTPVersion1_1];
[response setHeaderField:@"Content-Length" value:@"0"];
NSData *responseData = [self preprocessErrorResponse:response];
[asyncSocket writeData:responseData withTimeout:30 tag:90];
return nil;
}
NSLog(@"Vending websocket for consumer");
return [[MBIMUpdateQueue sharedInstance] vendUpdateWebSocketConsumerForRequest:request socket:asyncSocket];
}
return [super webSocketForURI:path];
}
- (BOOL)expectsRequestBodyFromMethod:(NSString *)method atPath:(NSString *)path
{
if ([method isEqualToString:@"POST"]) {
return YES;
}
return NO;
}
- (void)prepareForBodyWithSize:(UInt64)contentLength
{
_bodyData = [[NSMutableData alloc] initWithCapacity:contentLength];
}
- (void)processBodyData:(NSData *)postDataChunk
{
[_bodyData appendData:postDataChunk];
}
2018-11-17 01:07:55 -08:00
- (void)die
{
[_currentOperation cancel];
[super die];
}
@end