diff --git a/API/GNUmakefile b/API/GNUmakefile index df5225aed..861ba25e4 100644 --- a/API/GNUmakefile +++ b/API/GNUmakefile @@ -11,6 +11,7 @@ API_OBJC_FILES = \ SOGoAPIProduct.m \ SOGoAPI.m \ SOGoAPIVersion.m \ + SOGoAPIUserFolder.m \ SOGoAPIDispatcher.m API_RESOURCE_FILES += \ diff --git a/API/SOGoAPI.h b/API/SOGoAPI.h index 0d93f29fa..2dcf15942 100644 --- a/API/SOGoAPI.h +++ b/API/SOGoAPI.h @@ -24,8 +24,12 @@ #import #import +@class WOContext; + @interface SOGoAPI : NSObject - (NSArray *) methodAllowed; -- (NSDictionary *) action; +- (BOOL) needAuth; +- (NSArray *) paramNeeded; +- (NSDictionary *) action: (WOContext*) ctx withParam: (NSDictionary *) param; @end diff --git a/API/SOGoAPI.m b/API/SOGoAPI.m index aeb5b5281..6804db0e1 100644 --- a/API/SOGoAPI.m +++ b/API/SOGoAPI.m @@ -27,7 +27,17 @@ return result; } -- (NSDictionary *) action +- (BOOL) needAuth +{ + return YES; +} + +- (NSArray *) paramNeeded +{ + return nil; +} + +- (NSDictionary *) action: (WOContext*) ctx withParam: (NSDictionary *) param { NSDictionary* result; diff --git a/API/SOGoAPIDispatcher.m b/API/SOGoAPIDispatcher.m index 491c15c3e..47298a93a 100644 --- a/API/SOGoAPIDispatcher.m +++ b/API/SOGoAPIDispatcher.m @@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import #import +#import #import #import @@ -60,7 +61,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import #import -#import #import #import #import @@ -74,6 +74,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import #import + void handle_api_terminate(int signum) { NSLog(@"Forcing termination of API loop."); @@ -161,7 +162,49 @@ void handle_api_terminate(int signum) return action; } +- (NSDictionary *) _authBasicCheck: (NSString *) auth +{ + NSDictionary *user; + NSRange rng; + NSString *decodeCred, *domain, *login, *pwd; + SOGoUserManager *lm; + SOGoPasswordPolicyError perr; + int expire, grace; + BOOL rc; + user = nil; + + decodeCred = [[auth substringFromIndex:5] stringByTrimmingLeadWhiteSpaces]; + decodeCred = [decodeCred stringByDecodingBase64]; + + rng = [decodeCred rangeOfString:@":"]; + login = [decodeCred substringToIndex:rng.location]; + pwd = [decodeCred substringFromIndex:(rng.location + rng.length)]; + + domain = nil; + perr = PolicyNoError; + rc = ([[SOGoUserManager sharedUserManager] + checkLogin: [login stringByReplacingString: @"%40" + withString: @"@"] + password: pwd + domain: &domain + perr: &perr + expire: &expire + grace: &grace + additionalInfo: nil] + && perr == PolicyNoError); + + if(rc) + { + //Fecth user info + lm = [SOGoUserManager sharedUserManager]; + user = [lm contactInfosForUserWithUIDorEmail: login]; + } + else + user = nil; + + return user; +} - (NSException *) dispatchRequest: (WORequest*) theRequest inResponse: (WOResponse*) theResponse @@ -171,7 +214,8 @@ void handle_api_terminate(int signum) id activeUser; NSString *method, *action, *error; NSDictionary *form; - NSMutableDictionary *ret; + NSArray *paramNeeded; + NSMutableDictionary *paramInjected, *ret; NSBundle *bundle; id classAction; Class clazz; @@ -180,8 +224,6 @@ void handle_api_terminate(int signum) pool = [[NSAutoreleasePool alloc] init]; ASSIGN(context, theContext); - activeUser = [context activeUser]; - //Get the api action, check it action = [self _getActionFromUri: [theRequest uri]]; if(!action) @@ -189,6 +231,8 @@ void handle_api_terminate(int signum) error = [NSString stringWithFormat: @"No actions found for request to API: %@", [theRequest uri]]; [self errorWithFormat: error]; [self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 400]; + RELEASE(context); + RELEASE(pool); return nil; } @@ -200,6 +244,8 @@ void handle_api_terminate(int signum) error = [NSString stringWithFormat: @"No backend API found for action: %@", action]; [self errorWithFormat: error]; [self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 400]; + RELEASE(context); + RELEASE(pool); return nil; } @@ -213,21 +259,126 @@ void handle_api_terminate(int signum) error = [NSString stringWithFormat: @"Can't alloc and init class: %@", classAction]; [self errorWithFormat: error]; [self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 500]; + RELEASE(context); + RELEASE(pool); + return nil; } NS_ENDHANDLER; - - //Check user auth - - //Check method + method = [theRequest method]; + if(![[classAction methodAllowed] containsObject: method]) + { + error = [NSString stringWithFormat: @"Method %@ not allowed for action %@", method, action]; + [self errorWithFormat: error]; + [self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 400]; + RELEASE(context); + RELEASE(pool); + return nil; + } + paramInjected = [NSMutableDictionary dictionary]; //Check parameters + if((paramNeeded = [classAction paramNeeded]) && [paramNeeded count] >= 1) + { + NSDictionary* formAndQuery = [theRequest formValues]; + for(NSString *param in paramNeeded) + { + id value; + if((value = [formAndQuery objectForKey: param])) + { + NSString* trueValue; + if ([value isKindOfClass: [NSArray class]]) + trueValue = [value lastObject]; + else + trueValue = value; + [paramInjected setObject: trueValue forKey: param]; + } + else + { + error = [NSString stringWithFormat: @"Missing param %@ for action %@", param, action]; + [self errorWithFormat: error]; + [self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 400]; + RELEASE(context); + RELEASE(pool); + return nil; + } + } + } + if([classAction needAuth]) + { + if(debugOn) + [self logWithFormat: @"Check auth for action %@", action]; + //check auth + NSString *auth = [theRequest headerForKey: @"authorization"]; + if(auth) + { + NSDictionary* user; + if([[auth lowercaseString] hasPrefix: @"basic"]) + { + //basic auth + user = [self _authBasicCheck: auth]; + } + else if([[auth lowercaseString] hasPrefix: @"bearer"]) + { + //openid auth + + } + else + { + error = [NSString stringWithFormat: @"Authorization method incorrect: %@ for action %@", auth, action]; + [self errorWithFormat: error]; + [self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 401]; + RELEASE(context); + RELEASE(pool); + return nil; + } + + //add current user in paramInjected + if(user){ + if(debugOn) + [self logWithFormat: @"User authenticated %@", user]; + [paramInjected setObject: user forKey: @"user"]; + } + else + { + error = [NSString stringWithFormat: @"User wrong login or not found for action %@", action]; + [self errorWithFormat: error]; + [self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 401]; + RELEASE(context); + RELEASE(pool); + return nil; + } + } + else + { + error = [NSString stringWithFormat: @"No authorization header found for action %@", action]; + [self errorWithFormat: error]; + [self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 401]; + RELEASE(context); + RELEASE(pool); + return nil; + } + } //Execute action - ret = [classAction action]; + NS_DURING + { + ret = [classAction action: context withParam: paramInjected]; + } + NS_HANDLER + { + error = [NSString stringWithFormat: @"Internal error during: %@", action]; + [self errorWithFormat: error]; + [self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 500]; + RELEASE(context); + RELEASE(pool); + return nil; + } + NS_ENDHANDLER; + //Make the response [theResponse setContent: [ret jsonRepresentation]]; diff --git a/API/SOGoAPIUserFolder.h b/API/SOGoAPIUserFolder.h new file mode 100644 index 000000000..d58778eca --- /dev/null +++ b/API/SOGoAPIUserFolder.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2004-2005 SKYRIX Software AG + + This file is part of SOPE. + + SOPE is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + SOPE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with SOPE; see the file COPYING. If not, write to the + Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +#import +#import +#import +#import +#import + +@class WOContext; + +@interface SOGoAPIUserFolder : SOGoAPI +- (NSArray *) methodAllowed; +- (NSDictionary *) action: (WOContext*) ctx withParam: (NSDictionary *) param; +@end diff --git a/API/SOGoAPIUserFolder.m b/API/SOGoAPIUserFolder.m new file mode 100644 index 000000000..371d946ca --- /dev/null +++ b/API/SOGoAPIUserFolder.m @@ -0,0 +1,77 @@ +/* + Copyright (C) todo... +*/ + +#import + +#import + +@implementation SOGoAPIUserFolder + +- (id) init +{ + [super init]; + + return self; +} + +- (void) dealloc +{ + [super dealloc]; +} + + +- (NSDictionary *) action: (WOContext*) ctx withParam: (NSDictionary *) param +{ + /* + Coté sogo, il faudrait un endpoint API qui retourne tous les liens caldav/cardav + leur nom lisible de l’utilisateur. + */ +NSDictionary* result; +NSArray *folders; +NSMutableArray *cardavLinks, *caldavLinks; +NSString *serverUrl, *basePath, *c_uid, *url; +GCSFolderManager *fm; +int max, i; + +//Should be a user +c_uid = [[[param objectForKey: @"user"] objectForKey: @"emails"] objectAtIndex: 0]; + +//fetch folders +fm = [GCSFolderManager defaultFolderManager]; +basePath = [NSString stringWithFormat: @"/Users/%@", c_uid]; +folders = [fm listSubFoldersAtPath: basePath recursive: YES]; + +//Generate dav link +max = [folders count]; +serverUrl = [[ctx serverURL] absoluteString]; + +cardavLinks = [NSMutableArray array]; +caldavLinks = [NSMutableArray array]; +serverUrl = [[ctx serverURL] absoluteString]; +for (i = 0; i < max; i++) +{ + url = [NSString stringWithFormat: @"%@/SOGo/dav/%@/%@", serverUrl, c_uid, [folders objectAtIndex: i]]; + if([url rangeOfString:@"/Calendar/"].location == NSNotFound) + { + //Contacts + [cardavLinks addObject: url]; + } + else + { + //Calendar + [caldavLinks addObject: url]; + } +} + +result = [[NSDictionary alloc] initWithObjectsAndKeys: + c_uid, @"username", + cardavLinks, @"contact", + caldavLinks, @"calendar", + nil]; + +[result autorelease]; +return result; +} + + +@end /* SOGoAPIVersion */ \ No newline at end of file diff --git a/API/SOGoAPIVersion.h b/API/SOGoAPIVersion.h index 4e23dc79e..062cd156a 100644 --- a/API/SOGoAPIVersion.h +++ b/API/SOGoAPIVersion.h @@ -25,7 +25,9 @@ #import #import +@class WOContext; + @interface SOGoAPIVersion : SOGoAPI - (NSArray *) methodAllowed; -- (NSDictionary *) action; +- (NSDictionary *) action: (WOContext*) ctx withParam: (NSDictionary *) param; @end diff --git a/API/SOGoAPIVersion.m b/API/SOGoAPIVersion.m index d237dddee..4af1dd363 100644 --- a/API/SOGoAPIVersion.m +++ b/API/SOGoAPIVersion.m @@ -19,7 +19,13 @@ [super dealloc]; } -- (NSDictionary *) action { +- (BOOL) needAuth +{ + return NO; +} + +- (NSDictionary *) action: (WOContext*) ctx withParam: (NSDictionary *) param +{ NSDictionary* result; result = [[NSDictionary alloc] initWithObjectsAndKeys: diff --git a/SoObjects/SOGo/SOGoSystemDefaults.h b/SoObjects/SOGo/SOGoSystemDefaults.h index 3dce05cf6..a7e29c82c 100644 --- a/SoObjects/SOGo/SOGoSystemDefaults.h +++ b/SoObjects/SOGo/SOGoSystemDefaults.h @@ -83,6 +83,7 @@ static const NSString *kDisableSharingCalendar = @"Calendar"; - (BOOL) uixDebugEnabled; - (BOOL) easDebugEnabled; - (BOOL) openIdDebugEnabled; +- (BOOL) apiDebugEnabled; - (BOOL) tnefDecoderDebugEnabled; - (BOOL) xsrfValidationEnabled; diff --git a/SoObjects/SOGo/SOGoSystemDefaults.m b/SoObjects/SOGo/SOGoSystemDefaults.m index b47fbf6d6..abca1d906 100644 --- a/SoObjects/SOGo/SOGoSystemDefaults.m +++ b/SoObjects/SOGo/SOGoSystemDefaults.m @@ -654,6 +654,11 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict, return [self boolForKey: @"SOGoOpenIDDebugEnabled"]; } +- (BOOL) apiDebugEnabled +{ + return [self boolForKey: @"SOGoAPIDebugEnabled"]; +} + - (BOOL) tnefDecoderDebugEnabled { return [self boolForKey: @"SOGoTnefDecoderDebugEnabled"];