From: helge Date: Sun, 12 Mar 2006 23:41:40 +0000 (+0000) Subject: started to work on a Cookie based authenticator X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3c224f540c89965dcb6be9a959e968c84886a362;p=sope started to work on a Cookie based authenticator git-svn-id: http://svn.opengroupware.org/SOPE/trunk@1227 e4a50df8-12e2-0310-a44c-efbce7f8a7e3 --- diff --git a/sope-appserver/NGObjWeb/ChangeLog b/sope-appserver/NGObjWeb/ChangeLog index d9d73f1e..b6400efc 100644 --- a/sope-appserver/NGObjWeb/ChangeLog +++ b/sope-appserver/NGObjWeb/ChangeLog @@ -1,5 +1,11 @@ 2006-03-12 Helge Hess + * v4.5.223 + + * SoObjects: started SoCookieAuthenticator + + * SoObjects/SoHTTPAuthenticator.m: code cleanups + * SoObjects/SoProductLoader.m: quickfix to API (v4.5.222) * SoObjects: added new class SoProductLoader which can be used to diff --git a/sope-appserver/NGObjWeb/SoObjects/GNUmakefile b/sope-appserver/NGObjWeb/SoObjects/GNUmakefile index 2ee8983f..886a3ed5 100644 --- a/sope-appserver/NGObjWeb/SoObjects/GNUmakefile +++ b/sope-appserver/NGObjWeb/SoObjects/GNUmakefile @@ -41,6 +41,7 @@ SoObjects_HEADER_FILES = \ SoSecurityException.h \ SoComponent.h \ SoProductLoader.h \ + SoCookieAuthenticator.h \ SoObjects_OBJC_FILES = \ NSException+HTTP.m \ @@ -79,6 +80,7 @@ SoObjects_OBJC_FILES = \ SoSecurityException.m \ SoComponent.m \ SoProductLoader.m \ + SoCookieAuthenticator.m \ -include GNUmakefile.preamble include $(GNUSTEP_MAKEFILES)/subproject.make diff --git a/sope-appserver/NGObjWeb/SoObjects/SoCookieAuthenticator.h b/sope-appserver/NGObjWeb/SoObjects/SoCookieAuthenticator.h new file mode 100644 index 00000000..2f47456a --- /dev/null +++ b/sope-appserver/NGObjWeb/SoObjects/SoCookieAuthenticator.h @@ -0,0 +1,73 @@ +/* + Copyright (C) 2006 Helge Hess + + 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. +*/ + +#ifndef __SoObjects_SoCookieAuthenticator_H__ +#define __SoObjects_SoCookieAuthenticator_H__ + +#import + +/* + SoCookieAuthenticator + + SoCookieAuthenticator is an abstract base class for Cookie based + authentication. That is, it uses a Cookie to store the credentials + or a token of a session. + In the simplest case you only need to override -checkLogin:password: + to ensure login/password combinations. + + NOTE: work in progress. + + TODO: we also need a WOSessionAuthenticator which assumes that the + existance of a session implies a successful authentication? +*/ + +@class NSString, NSException, NSArray; +@class WOContext, WOResponse, WOCookie; +@class SoUser; + +@interface SoCookieAuthenticator : NSObject +{ +} + +/* password checker (override in subclasses !) */ + +- (BOOL)checkLogin:(NSString *)_login password:(NSString *)_pwd; + +/* Cookie authentication */ + +- (NSString *)cookieNameInContext:(WOContext *)_ctx; +- (WOResponse *)preprocessCredentialsInContext:(WOContext *)_ctx; + +- (NSString *)checkCredentialsInContext:(WOContext *)_ctx; +- (NSArray *)parseCredentials:(NSString *)_creds; + +/* user management */ + +- (SoUser *)userInContext:(WOContext *)_ctx; +- (NSArray *)rolesForLogin:(NSString *)_login; + +/* render auth exceptions of SoSecurityManager */ + +- (BOOL)renderException:(NSException *)_e inContext:(WOContext *)_ctx; + +@end + +#endif /* __SoObjects_SoCookieAuthenticator_H__ */ diff --git a/sope-appserver/NGObjWeb/SoObjects/SoCookieAuthenticator.m b/sope-appserver/NGObjWeb/SoObjects/SoCookieAuthenticator.m new file mode 100644 index 00000000..4feebd0b --- /dev/null +++ b/sope-appserver/NGObjWeb/SoObjects/SoCookieAuthenticator.m @@ -0,0 +1,271 @@ +/* + Copyright (C) 2006 Helge Hess + + 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. +*/ + +#include "SoCookieAuthenticator.h" +#include "SoHTTPAuthenticator.h" +#include "SoUser.h" +#include "SoPermissions.h" +#include "NSException+HTTP.h" +#include +#include +#include +#include +#include +#include +#include "common.h" + +#if APPLE_RUNTIME || NeXT_RUNTIME +@interface NSObject(Miss) +- (void)subclassResponsibility:(SEL)cmd; +@end +#endif + +// TODO: do we have 'anonymous' in such a scenario??? +// TODO: we want to redirect to a login panel and include the root URL +// using a query path + +@implementation SoCookieAuthenticator + +static NSString *prefix = @"0xHIGHFLYx"; + ++ (int)version { + return 1; +} + +/* HTTP basic authentication */ + +- (NSString *)cookieNameInContext:(WOContext *)_ctx { + return [prefix stringByAppendingString:[[_ctx application] name]]; +} + +/* check for roles */ + +- (BOOL)checkLogin:(NSString *)_login password:(NSString *)_pwd { + [self subclassResponsibility:_cmd]; + return NO; +} + +- (NSArray *)parseCredentials:(NSString *)_creds { + if (![_creds isNotEmpty]) + return nil; + + /* per default we just reuse HTTP basic credentials! */ + return [SoHTTPAuthenticator parseCredentials:_creds]; +} + +- (NSString *)checkCredentials:(NSString *)_creds { + /* checks credentials, returnes login if successful */ + NSString *login, *pwd; + NSArray *creds; + + if (![(creds = [self parseCredentials:_creds]) isNotEmpty]) + return nil; + + login = [creds objectAtIndex:0]; + if ([login isEqualToString:@"anonymous"]) + return @"anonymous"; + + pwd = [creds objectAtIndex:1]; + if (![self checkLogin:login password:pwd]) + return nil; + + return login; +} + +- (NSString *)checkCredentialsInContext:(WOContext *)_ctx { + WORequest *rq; + NSString *auth; + + rq = [_ctx request]; + + auth = [rq cookieValueForKey:[self cookieNameInContext:_ctx]]; + if (![auth isNotEmpty]) { + /* no auth supplied */ + return @"anonymous"; + } + return [self checkCredentials:auth]; +} + +- (NSArray *)rolesForLogin:(NSString *)_login { + // TODO: I suppose this should be overridden? + NSArray *uroles; + + // could add manager of login=root + + uroles = [NSArray arrayWithObjects: + SoRole_Authenticated, + SoRole_Anonymous, + nil]; + return uroles; +} + +- (SoUser *)userWithLogin:(NSString *)_login andRoles:(NSArray *)_roles + inContext:(WOContext *)_ctx +{ + /* the actual factory method */ + return [[[SoUser alloc] initWithLogin:_login roles:_roles] autorelease]; +} + +- (SoUser *)userInContext:(WOContext *)_ctx { + static SoUser *anonymous = nil; + NSString *login; + NSArray *uroles; + + if (anonymous == nil) { + NSArray *ar = [NSArray arrayWithObject:SoRole_Anonymous]; + anonymous = [[SoUser alloc] initWithLogin:@"anonymous" roles:ar]; + } + + if ((login = [self checkCredentialsInContext:_ctx]) == nil) + /* some error (otherwise result would have been anonymous */ + return nil; + + if ([login isEqualToString:@"anonymous"]) + return anonymous; + + uroles = [self rolesForLogin:login]; + return [self userWithLogin:login andRoles:uroles inContext:_ctx]; +} + +/* auth fail handling */ + +- (void)setupAuthFailResponse:(WOResponse *)_response + withReason:(NSString *)_reason inContext:(WOContext *)_ctx +{ + [_response appendContentString:@"TODO: render login page ..."]; +} + +- (WOResponse *)unauthorized:(NSString *)_reason inContext:(WOContext *)_ctx { + WOResponse *r; + + if (![_reason isNotEmpty]) _reason = @"Unauthorized"; + + r = [_ctx response]; + [self setupAuthFailResponse:r withReason:_reason inContext:_ctx]; + return r; +} + +- (BOOL)renderException:(NSException *)_e inContext:(WOContext *)_ctx { + /* + TODO: this can be called for content which is not accessible to the + user? (but the user is otherwise perfectly ok?) + Should not: in this case we should get a 404? + */ + if ([_e httpStatus] != 401) + return NO; + + [self setupAuthFailResponse:[_ctx response] + withReason:[_e reason] inContext:_ctx]; + return YES; +} + +/* request preprocessing */ + +- (WOResponse *)preprocessCredentialsInContext:(WOContext *)_ctx { + /* + This is called by SoObjectRequestHandler prior doing any significant + processing to allow the authenticator to reject invalid requests. + */ + WOResponse *r; + NSString *auth; + NSString *k; + NSString *user, *pwd; + NSRange rng; + + auth = [[_ctx request] cookieValueForKey:[self cookieNameInContext:_ctx]]; + if (![auth isNotEmpty]) { + /* no authentication provided */ + static NSArray *anon = nil; + if (anon == nil) + anon = [[NSArray alloc] initWithObjects:SoRole_Anonymous, nil]; + + [_ctx setObject:anon forKey:@"SoAuthenticatedRoles"]; + return nil; + } + + /* authentication provided, check whether it's valid */ + + r = [_ctx response]; + if ([auth length] < 6) { + [self logWithFormat:@"tried unknown authentication method: %@ (A)", auth]; + return [self unauthorized:@"unsupported authentication method" + inContext:_ctx]; + } + k = [[auth substringToIndex:5] lowercaseString]; + if (![k hasPrefix:@"basic"]) { + [self logWithFormat:@"tried unknown authentication method: %@ (B)", auth]; + return [self unauthorized:@"unsupported authentication method" + inContext:_ctx]; + } + + k = [auth substringFromIndex:6]; + if ((k = [k stringByDecodingBase64]) == nil) { + [self logWithFormat:@"tried unknown authentication method: %@ (C)", auth]; + return [self unauthorized:@"unsupported authentication method" + inContext:_ctx]; + } + + rng = [k rangeOfString:@":"]; + if (rng.length <= 0) { + [self logWithFormat:@"got malformed basic credentials (missing colon)!"]; + return [self unauthorized:@"malformed basic credentials!" inContext:_ctx]; + } + + user = [k substringToIndex:rng.location]; + pwd = [k substringFromIndex:(rng.location + rng.length)]; + + rng = [user rangeOfString:@"\\"]; + if (rng.length > 0) { + [self debugWithFormat:@"splitting of domain in user: '%@'", user]; + user = [user substringFromIndex:(rng.location + rng.length)]; + } + + if (![user isNotEmpty]) { + [self logWithFormat:@"got malformed basic credentials!"]; + return [self unauthorized:@"empty login in credentials?" inContext:_ctx]; + } + if (![pwd isNotEmpty]) { + [self logWithFormat:@"got empty password for user '%@'!", user]; + return [self unauthorized:@"empty passwords unsupported!" inContext:_ctx]; + } + + /* authenticate valid credentials */ + + if (![self checkLogin:user password:pwd]) { + [self logWithFormat:@"tried wrong password for user '%@'!", user]; + return [self unauthorized:nil inContext:_ctx]; + } + + //[self debugWithFormat:@"authenticated user '%@'", user]; + + /* authentication succeeded */ + { + static NSArray *auth = nil; + if (auth == nil) { + auth = [[NSArray alloc] initWithObjects: + SoRole_Authenticated, SoRole_Anonymous, nil]; + } + [_ctx setObject:auth forKey:@"SoAuthenticatedRoles"]; + } + return nil; +} + +@end /* SoCookieAuthenticator */ diff --git a/sope-appserver/NGObjWeb/SoObjects/SoHTTPAuthenticator.m b/sope-appserver/NGObjWeb/SoObjects/SoHTTPAuthenticator.m index c7c6de3a..d8f8b15d 100644 --- a/sope-appserver/NGObjWeb/SoObjects/SoHTTPAuthenticator.m +++ b/sope-appserver/NGObjWeb/SoObjects/SoHTTPAuthenticator.m @@ -1,5 +1,7 @@ /* - Copyright (C) 2002-2005 SKYRIX Software AG + Copyright (C) + 2002-2006 SKYRIX Software AG + 2006 Helge Hess This file is part of SOPE. @@ -67,7 +69,7 @@ NSString *login, *pwd; NSString *k; - if ([_creds length] == 0) { + if (![_creds isNotEmpty]) { static NSArray *anon = nil; if (anon == nil) anon = [[NSArray alloc] initWithObjects:@"anonymous", @"", nil]; @@ -112,9 +114,9 @@ NSString *login, *pwd; NSArray *creds; - if ((creds = [self parseCredentials:_creds]) == nil) + if (![(creds = [self parseCredentials:_creds]) isNotEmpty]) return nil; - + login = [creds objectAtIndex:0]; if ([login isEqualToString:@"anonymous"]) return @"anonymous"; @@ -146,7 +148,7 @@ uroles = [NSArray arrayWithObjects: SoRole_Authenticated, SoRole_Anonymous, - nil]; + nil]; return uroles; } @@ -175,7 +177,7 @@ WOResponse *r; NSString *auth; - if ([_reason length] == 0) _reason = @"Unauthorized"; + if (![_reason isNotEmpty]) _reason = @"Unauthorized"; auth = [NSString stringWithFormat:@"basic realm=\"%@\"", [self authRealmInContext:_ctx]]; @@ -241,11 +243,11 @@ user = [user substringFromIndex:(rng.location + rng.length)]; } - if ([user length] == 0) { + if (![user isNotEmpty]) { [self logWithFormat:@"got malformed basic credentials!"]; return [self unauthorized:@"empty login in credentials?" inContext:_ctx]; } - if ([pwd length] == 0) { + if (![pwd isNotEmpty]) { [self logWithFormat:@"got empty password for user '%@'!", user]; return [self unauthorized:@"empty passwords unsupported!" inContext:_ctx]; } diff --git a/sope-appserver/NGObjWeb/Version b/sope-appserver/NGObjWeb/Version index 9b6f14d6..3f449745 100644 --- a/sope-appserver/NGObjWeb/Version +++ b/sope-appserver/NGObjWeb/Version @@ -1,6 +1,6 @@ # version file -SUBMINOR_VERSION:=222 +SUBMINOR_VERSION:=223 # v4.5.214 requires libNGExtensions v4.5.179 # v4.5.122 requires libNGExtensions v4.5.153 diff --git a/sope-appserver/samples/SoCookieAuth/Application.m b/sope-appserver/samples/SoCookieAuth/Application.m new file mode 100644 index 00000000..d5fdba5e --- /dev/null +++ b/sope-appserver/samples/SoCookieAuth/Application.m @@ -0,0 +1,55 @@ +/* + Copyright (C) 2006 Helge Hess + + 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. +*/ + +#include + +@interface SoCookieAuth : SoApplication +{ +} + +@end + +#include +#include "common.h" + +@implementation SoCookieAuth + +- (id)authenticatorInContext:(id)_ctx { + return [[[SoCookieAuthenticator alloc] init] autorelease]; +} + +@end /* SoCookieAuth */ + + + +int main(int argc, char **argv, char **env) { + NSAutoreleasePool *pool; + + pool = [[NSAutoreleasePool alloc] init]; +#if LIB_FOUNDATION_LIBRARY + [NSProcessInfo initializeWithArguments:argv count:argc environment:env]; +#endif + + WOApplicationMain(@"SoCookieAuth", argc, (void*)argv); + + [pool release]; + return 0; +} diff --git a/sope-appserver/samples/SoCookieAuth/ChangeLog b/sope-appserver/samples/SoCookieAuth/ChangeLog new file mode 100644 index 00000000..a6b5dd01 --- /dev/null +++ b/sope-appserver/samples/SoCookieAuth/ChangeLog @@ -0,0 +1,4 @@ +2005-07-10 Helge Hess + + * created ChangeLog / project + diff --git a/sope-appserver/samples/SoCookieAuth/GNUmakefile b/sope-appserver/samples/SoCookieAuth/GNUmakefile new file mode 100644 index 00000000..3ae9ece3 --- /dev/null +++ b/sope-appserver/samples/SoCookieAuth/GNUmakefile @@ -0,0 +1,13 @@ +# GNUstep makefile + +-include ../../../config.make +include $(GNUSTEP_MAKEFILES)/common.make + +WOAPP_NAME = SoCookieAuth + +SoCookieAuth_OBJC_FILES += Application.m Main.m +SoCookieAuth_COMPONENTS += Main.wo + +-include GNUmakefile.preamble +include $(GNUSTEP_MAKEFILES)/woapp.make +-include GNUmakefile.postamble diff --git a/sope-appserver/samples/SoCookieAuth/GNUmakefile.preamble b/sope-appserver/samples/SoCookieAuth/GNUmakefile.preamble new file mode 100644 index 00000000..5e85b30f --- /dev/null +++ b/sope-appserver/samples/SoCookieAuth/GNUmakefile.preamble @@ -0,0 +1,14 @@ +# compile settings + +APPSERVER="../.." + +ADDITIONAL_LIB_DIRS += \ + -L$(APPSERVER)/NGObjWeb/$(GNUSTEP_OBJ_DIR) + +# static references required for Mach linker + +ADDITIONAL_TOOL_LIBS += \ + -lEOControl \ + -lDOM -lXmlRpc -lSaxObjC + +ADDITIONAL_LIB_DIRS += -L/usr/local/lib -L/usr/lib diff --git a/sope-appserver/samples/SoCookieAuth/Main.m b/sope-appserver/samples/SoCookieAuth/Main.m new file mode 100644 index 00000000..cdb743d3 --- /dev/null +++ b/sope-appserver/samples/SoCookieAuth/Main.m @@ -0,0 +1,57 @@ +/* + Copyright (C) 2006 Helge Hess + + 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. +*/ + +#include + +@class NSArray; + +@interface Main : WOComponent +{ +} + +@end + +#include "common.h" + +@implementation Main + +- (id)initWithContext:(WOContext *)_ctx { + if ((self = [super initWithContext:_ctx]) != nil) { + } + return self; +} + +- (void)dealloc { + [super dealloc]; +} + +/* accessors */ + +#if 0 +- (void)setItem:(id)_item { + ASSIGN(self->item, _item); +} +- (id)item { + return self->item; +} +#endif + +@end /* Main */ diff --git a/sope-appserver/samples/SoCookieAuth/README b/sope-appserver/samples/SoCookieAuth/README new file mode 100644 index 00000000..399ced29 --- /dev/null +++ b/sope-appserver/samples/SoCookieAuth/README @@ -0,0 +1,3 @@ +WORK IN PROGRESS + +App to test SOPE cookie authentication (SoCookieAuthenticator) diff --git a/sope-appserver/samples/SoCookieAuth/common.h b/sope-appserver/samples/SoCookieAuth/common.h new file mode 100644 index 00000000..ac5c945b --- /dev/null +++ b/sope-appserver/samples/SoCookieAuth/common.h @@ -0,0 +1,23 @@ +/* + Copyright (C) 2006 Helge Hess + + 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 +#include