From 7bb57b46927f02f1a2d30507cdb18878dd3e44ee Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 11 Sep 2009 14:38:43 +0000 Subject: [PATCH] Monotone-Parent: 78e8177b7871651ba9b05f971e1b40376fb5e49d Monotone-Revision: 8e4f10d6d171bd7c241c05d63d20efa7f2a66de4 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2009-09-11T14:38:43 Monotone-Branch: ca.inverse.sogo --- Apache/SOGo.conf | 61 +++++++-- ChangeLog | 33 +++++ Main/SOGo.m | 16 ++- SoObjects/SOGo/GNUmakefile | 2 + SoObjects/SOGo/SOGoCache.h | 3 +- SoObjects/SOGo/SOGoCache.m | 7 +- SoObjects/SOGo/SOGoDAVAuthenticator.h | 6 +- SoObjects/SOGo/SOGoDAVAuthenticator.m | 1 - SoObjects/SOGo/SOGoProxyAuthenticator.h | 56 ++++++++ SoObjects/SOGo/SOGoProxyAuthenticator.m | 166 ++++++++++++++++++++++++ SoObjects/SOGo/SOGoUser.m | 2 +- UI/Common/UIxPageFrame.m | 21 ++- UI/MainUI/SOGoRootPage.m | 3 +- UI/MainUI/SOGoUserHomePage.m | 26 ++-- UI/Templates/UIxPageFrame.wox | 6 +- 15 files changed, 367 insertions(+), 42 deletions(-) create mode 100644 SoObjects/SOGo/SOGoProxyAuthenticator.h create mode 100644 SoObjects/SOGo/SOGoProxyAuthenticator.m diff --git a/Apache/SOGo.conf b/Apache/SOGo.conf index 493e60396..18e1efcb3 100644 --- a/Apache/SOGo.conf +++ b/Apache/SOGo.conf @@ -1,9 +1,17 @@ -Alias /sogo.woa/WebServerResources/ \ - /usr/GNUstep/System/Library/SOGo/WebServerResources/ Alias /SOGo.woa/WebServerResources/ \ /usr/GNUstep/System/Library/SOGo/WebServerResources/ -Alias /SOGO.woa/WebServerResources/ \ +Alias /SOGo/WebServerResources/ \ /usr/GNUstep/System/Library/SOGo/WebServerResources/ +AliasMatch /SOGo/so/ControlPanel/Products/(.*)/Resources/(.*) \ + /usr/GNUstep/System/Library/SOGo/$1.SOGo/Resources/$2 + +## For Debian-based distributions, use the following instead of the above: +# Alias /SOGo.woa/WebServerResources/ \ +# /usr/lib/GNUstep/SOGo/WebServerResources/ +# Alias /SOGo/WebServerResources/ \ +# /usr/lib/GNUstep/SOGo/WebServerResources/ +# AliasMatch /SOGo/so/ControlPanel/Products/(.*)/Resources/(.*) \ +# /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2 SetHandler default-handler @@ -25,20 +33,45 @@ Alias /SOGO.woa/WebServerResources/ \ SetHandler default-handler -AliasMatch /SOGo/so/ControlPanel/Products/(.*)/Resources/(.*) \ - /usr/GNUstep/System/Library/SOGo/$1.SOGo/Resources/$2 +## Uncomment the following to enable proxy-side authentication, you will then +## need to set the "SOGoTrustProxyAuthentication" SOGo user default to YES and +## adjust the "x-webobjects-remote-user" proxy header in the "Proxy" section +## below. +# +# AuthType XXX +# Require valid-user +# SetEnv proxy-nokeepalive 1 +# Allow from all +# + +ProxyRequests Off +SetEnv proxy-nokeepalive 1 +ProxyPreserveHost On +ProxyPass /SOGo balancer://sogocluster/SOGo - BalancerMember http://127.0.0.1:20000 retry=1 max=1 timeout=120 -# If you enable those, don't forget the enable the spawning of multiple SOGo -# processes. With Redhat-based distributions, this is done by setting the -# "PREFORK" variable in /etc/sysconfig/sogo to the amount of processes as -# value. + RequestHeader set "x-webobjects-server-protocol" "HTTP/1.0" + RequestHeader set "x-webobjects-remote-addr" "127.0.0.1" + RequestHeader set "x-webobjects-remote-host" "127.0.0.1" + +## adjust the following to your configuration + RequestHeader set "x-webobjects-server-port" "443" + RequestHeader set "x-webobjects-server-name" "yourhostname" + RequestHeader set "x-webobjects-server-url" "https://yourhostname" + +## When using proxy-side autentication, you need to uncomment and +## adjust the following line: +# RequestHeader set "x-webobjects-remote-user" "%{WEBAUTH_USER}e" + + BalancerMember http://127.0.0.1:30000 retry=1 max=1 timeout=120 +## If you enable those, don't forget the enable the spawning of multiple SOGo +## processes. With Redhat-based distributions, this is done by setting the +## "PREFORK" variable in /etc/sysconfig/sogo to the amount of processes. +## In Debian-based distributions, you should do the same in the +## /etc/default/sogo file instead. # BalancerMember http://127.0.0.1:20001 retry=1 max=1 timeout=120 # BalancerMember http://127.0.0.1:20002 retry=1 max=1 timeout=120 ProxySet lbmethod=byrequests maxattempts=1 + Order allow,deny + Allow from all - -SetEnv proxy-nokeepalive 1 -ProxyRequests Off -ProxyPass /SOGo balancer://sogocluster/SOGo diff --git a/ChangeLog b/ChangeLog index 7f5431318..9e9877b63 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,36 @@ +2009-09-11 Wolfgang Sourdeau + + * UI/MainUI/SOGoUserHomePage.m (-logoffAction): adjusted to handle + the case where then authenticator doesn't handle authentication + cookies. In such cases, this method should theorically never be + invoked though. + + * UI/Common/UIxPageFrame.m (-canLogoff): new bool accessor that + returns whether the current authenticator handles user + disconnection or not, in which case we hide the "logoff" link in + the UIxPageFrame. + + * SoObjects/SOGo/SOGoCache.m (-registerUser:withName:): adjusted + method to take an additional "userName" parameter in order to + store users with the username used at login time rather than their + user id. This is especially useful when using indirect binds. + + * SoObjects/SOGo/LDAPSource.m (-_qualifierForUIDFilter:): we + include the bindFields in the request if they are specified in the + configuration. + + * Main/SOGo.m (-authenticatorInContext:): we no return an instance + of SOGoProxyAuthenticator if "SOGoTrustProxyAuthentication" is + set in the configuration. + + * SoObjects/SOGo/SOGoProxyAuthenticator.[mh]: new class module + that defines an authenticator that unconditionnaly trusts and + handle authentication done proxy-side. SOGo uses that + authenticator when the "SOGoTrustProxyAuthentication" user default + is set. The username is taken from the "x-webobjects-remote-user" + http header, you may have to adapt your proxy configuration + accordingly. + 2009-09-10 Francis Lachapelle * UI/Scheduler/UIxComponentEditor.m ([UIxComponentEditor diff --git a/Main/SOGo.m b/Main/SOGo.m index 711b1bc73..3e47ce0b9 100644 --- a/Main/SOGo.m +++ b/Main/SOGo.m @@ -48,6 +48,7 @@ #import #import #import +#import #import #import #import @@ -67,6 +68,8 @@ static BOOL hasCheckedTables = NO; static BOOL debugRequests = NO; static BOOL debugLeaks = NO; +static BOOL trustProxyAuthentication; + #ifdef GNUSTEP_BASE_LIBRARY static BOOL debugObjectAllocation = NO; #endif @@ -120,6 +123,8 @@ static BOOL debugObjectAllocation = NO; /* require Authenticated role for View and WebDAV */ [sInfo declareRoles: basicRoles asDefaultForPermission: SoPerm_View]; [sInfo declareRoles: basicRoles asDefaultForPermission: SoPerm_WebDAVAccess]; + + trustProxyAuthentication = [ud boolForKey: @"SOGoTrustProxyAuthentication"]; } - (id) init @@ -269,10 +274,15 @@ static BOOL debugObjectAllocation = NO; { id authenticator; - if ([[context request] handledByDefaultHandler]) - authenticator = [SOGoWebAuthenticator sharedSOGoWebAuthenticator]; + if (trustProxyAuthentication) + authenticator = [SOGoProxyAuthenticator sharedSOGoProxyAuthenticator]; else - authenticator = [SOGoDAVAuthenticator sharedSOGoDAVAuthenticator]; + { + if ([[context request] handledByDefaultHandler]) + authenticator = [SOGoWebAuthenticator sharedSOGoWebAuthenticator]; + else + authenticator = [SOGoDAVAuthenticator sharedSOGoDAVAuthenticator]; + } return authenticator; } diff --git a/SoObjects/SOGo/GNUmakefile b/SoObjects/SOGo/GNUmakefile index 335778d72..c57eca0b8 100644 --- a/SoObjects/SOGo/GNUmakefile +++ b/SoObjects/SOGo/GNUmakefile @@ -45,6 +45,7 @@ SOGo_HEADER_FILES = \ \ SOGoAuthenticator.h \ SOGoDAVAuthenticator.h \ + SOGoProxyAuthenticator.h \ SOGoWebAuthenticator.h \ SOGoWebDAVAclManager.h \ SOGoWebDAVValue.h \ @@ -94,6 +95,7 @@ SOGo_OBJC_FILES = \ NSURL+DAV.m \ \ SOGoDAVAuthenticator.m \ + SOGoProxyAuthenticator.m \ SOGoWebAuthenticator.m \ SOGoWebDAVAclManager.m \ SOGoWebDAVValue.m \ diff --git a/SoObjects/SOGo/SOGoCache.h b/SoObjects/SOGo/SOGoCache.h index bbb806e3c..820b22b37 100644 --- a/SoObjects/SOGo/SOGoCache.h +++ b/SoObjects/SOGo/SOGoCache.h @@ -55,7 +55,8 @@ - (id) objectNamed: (NSString *) name inContainer: (SOGoObject *) container; -- (void) registerUser: (SOGoUser *) user; +- (void) registerUser: (SOGoUser *) user + withName: (NSString *) userName; - (id) userNamed: (NSString *) name; - (NSMutableDictionary *) userAttributesForLogin: (NSString *) theLogin; diff --git a/SoObjects/SOGo/SOGoCache.m b/SoObjects/SOGo/SOGoCache.m index 21e1e1a1a..e5db1cbc4 100644 --- a/SoObjects/SOGo/SOGoCache.m +++ b/SoObjects/SOGo/SOGoCache.m @@ -216,11 +216,12 @@ static NSLock *lock; } - (void) registerUser: (SOGoUser *) user + withName: (NSString *) userName { #if defined(THREADSAFE) [lock lock]; #endif - [users setObject: user forKey: [user login]]; + [users setObject: user forKey: userName]; #if defined(THREADSAFE) [lock unlock]; #endif @@ -264,11 +265,11 @@ static NSLock *lock; forLogin: (NSString *) theLogin { NSDictionary *d; - memcached_return rc; const char *key; char *s; - unsigned int len, vlen, flags; + unsigned int len, flags; + size_t vlen; if (!handle) return nil; diff --git a/SoObjects/SOGo/SOGoDAVAuthenticator.h b/SoObjects/SOGo/SOGoDAVAuthenticator.h index 21e06a8af..3adfb3c13 100644 --- a/SoObjects/SOGo/SOGoDAVAuthenticator.h +++ b/SoObjects/SOGo/SOGoDAVAuthenticator.h @@ -19,8 +19,8 @@ 02111-1307, USA. */ -#ifndef __Main_SOGoDAVAuthenticator_H__ -#define __Main_SOGoDAVAuthenticator_H__ +#ifndef _SOGoDAVAuthenticator_H__ +#define _SOGoDAVAuthenticator_H__ #import @@ -50,4 +50,4 @@ @end -#endif /* __Main_SOGoDAVAuthenticator_H__ */ +#endif /* _SOGoDAVAuthenticator_H__ */ diff --git a/SoObjects/SOGo/SOGoDAVAuthenticator.m b/SoObjects/SOGo/SOGoDAVAuthenticator.m index 0b59e1459..0288814fe 100644 --- a/SoObjects/SOGo/SOGoDAVAuthenticator.m +++ b/SoObjects/SOGo/SOGoDAVAuthenticator.m @@ -27,7 +27,6 @@ #import #import #import -#import #import "LDAPUserManager.h" #import "SOGoPermissions.h" diff --git a/SoObjects/SOGo/SOGoProxyAuthenticator.h b/SoObjects/SOGo/SOGoProxyAuthenticator.h new file mode 100644 index 000000000..7ee03dd9a --- /dev/null +++ b/SoObjects/SOGo/SOGoProxyAuthenticator.h @@ -0,0 +1,56 @@ +/* SOGoProxyAuthenticator.h - this file is part of SOGo + * + * Copyright (C) 2009 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _SOGoProxyAuthenticator_H__ +#define _SOGoProxyAuthenticator_H__ + +#import +#import + +#import "SOGoAuthenticator.h" + +/* + SOGoProxyAuthenticator + + This just overrides the login/pwd check method and always returns YES since + the password is already checked in Apache. +*/ + +@class NSUserDefaults; +@class NSString; + +@class SOGoUser; + +// @interface SOGoProxyAuthenticator : SoHTTPAuthenticator +@interface SOGoProxyAuthenticator : NSObject +{ + NSString *authMethod; +} + ++ (id) sharedSOGoProxyAuthenticator; + +- (SOGoUser *) userInContext: (WOContext *) _ctx; +- (NSString *) passwordInContext: (WOContext *) context; + +@end + +#endif /* _SOGoProxyAuthenticator_H__ */ diff --git a/SoObjects/SOGo/SOGoProxyAuthenticator.m b/SoObjects/SOGo/SOGoProxyAuthenticator.m new file mode 100644 index 000000000..5ff6a6817 --- /dev/null +++ b/SoObjects/SOGo/SOGoProxyAuthenticator.m @@ -0,0 +1,166 @@ +/* SOGoProxyAuthenticator.h - this file is part of SOGo + * + * Copyright (C) 2009 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import + +#import +#import +#import +#import + +#import + +#import "LDAPUserManager.h" +#import "SOGoPermissions.h" +#import "SOGoUser.h" + +#import "SOGoProxyAuthenticator.h" + +@implementation SOGoProxyAuthenticator + ++ (id) sharedSOGoProxyAuthenticator +{ + static SOGoProxyAuthenticator *auth = nil; + + if (!auth) + auth = [self new]; + + return auth; +} + +- (id) init +{ + NSUserDefaults *ud; + + if ((self = [super init])) + { + ud = [NSUserDefaults standardUserDefaults]; + authMethod = [ud stringForKey: @"SOGoAuthenticationMethod"]; + if (!authMethod) + authMethod = [ud stringForKey: @"SOGoAuthentificationMethod"]; + if (!authMethod) + { + authMethod = @"LDAP"; + [self warnWithFormat: + @"authentication method automatically set to '%@'", + authMethod]; + } + } + + return self; +} + +- (void) dealloc +{ + [authMethod release]; + [super dealloc]; +} + +- (BOOL) checkLogin: (NSString *) _login + password: (NSString *) _pwd +{ + return YES; +} + +/* create SOGoUser */ + +- (NSString *) checkCredentialsInContext: (WOContext *) context +{ + NSString *remoteUser; + + remoteUser = [[context request] headerForKey: @"x-webobjects-remote-user"]; + + return remoteUser; +} + +- (WOResponse *) unauthorized: (NSString *) reason + inContext: (WOContext *) context +{ + WOResponse *r; + + if (![reason length]) + reason = @"Unauthorized"; + + r = [context response]; + [r setStatus: 403 /* unauthorized */]; + [r setHeader: @"text/plain; charset=utf-8" forKey: @"content-type"]; + [r appendContentString: reason]; + + return r; +} + +- (SOGoUser *) userInContext: (WOContext *) context +{ + SOGoUser *user; + NSString *login; + + login = [self checkCredentialsInContext: context]; + if ([login length]) + user = [SOGoUser userWithLogin: login + roles: [NSArray arrayWithObject: + SoRole_Authenticated]]; + else + user = nil; + + return user; +} + +- (NSString *) passwordInContext: (WOContext *) context +{ + return @""; +} + +- (WOResponse *) preprocessCredentialsInContext: (WOContext *) context +{ + WOResponse *r; + + if ([self userInContext: context]) + { + [context setObject: [NSArray arrayWithObject: SoRole_Authenticated] + forKey: @"SoAuthenticatedRoles"]; + r = nil; + } + else + r = [self unauthorized: nil inContext: context]; + + return r; +} + +- (BOOL) renderException: (NSException *) e + inContext: (WOContext *) context +{ + BOOL rc; + + if ([e httpStatus] == 401) + { + [self unauthorized: [e reason] inContext: context]; + rc = YES; + } + else + rc = NO; + + return rc; +} + +@end /* SOGoProxyAuthenticator */ diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index 7d3691ae9..23cb2bdfd 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -246,7 +246,7 @@ _timeValue (NSString *key) if (user) { [user autorelease]; - [cache registerUser: user]; + [cache registerUser: user withName: newLogin]; } } if (newRoles) diff --git a/UI/Common/UIxPageFrame.m b/UI/Common/UIxPageFrame.m index ae51cc44b..83f4d5d71 100644 --- a/UI/Common/UIxPageFrame.m +++ b/UI/Common/UIxPageFrame.m @@ -25,9 +25,10 @@ #import -#import -#import -#import +#import +#import +#import +#import #import "UIxPageFrame.h" @@ -389,6 +390,20 @@ static NSString *siteFavicon = nil; && [user isSuperUser]); } +- (BOOL) canLogoff +{ + BOOL canLogoff; + id auth; + + auth = [[self clientObject] authenticatorInContext: context]; + if ([auth respondsToSelector: @selector (cookieNameInContext:)]) + canLogoff = ([[auth cookieNameInContext: context] length] > 0); + else + canLogoff = NO; + + return canLogoff; +} + - (BOOL) userHasCalendarAccess { SOGoUser *user; diff --git a/UI/MainUI/SOGoRootPage.m b/UI/MainUI/SOGoRootPage.m index 62ab73802..4c50706a0 100644 --- a/UI/MainUI/SOGoRootPage.m +++ b/UI/MainUI/SOGoRootPage.m @@ -121,7 +121,8 @@ static NSArray *supportedLanguages = nil; oldLocation = [[self clientObject] baseURLInContext: context]; response = [self redirectToLocation: [NSString stringWithFormat: @"%@/%@", - oldLocation, login]]; + oldLocation, + [login stringByEscapingURL]]]; } return response; diff --git a/UI/MainUI/SOGoUserHomePage.m b/UI/MainUI/SOGoUserHomePage.m index d81ba178c..4ec067cce 100644 --- a/UI/MainUI/SOGoUserHomePage.m +++ b/UI/MainUI/SOGoUserHomePage.m @@ -270,7 +270,7 @@ static NSString *LDAPContactInfoAttribute = nil; SOGoWebAuthenticator *auth; id container; NSCalendarDate *date; - NSString *userName; + NSString *userName, *cookieName; container = [[self clientObject] container]; @@ -281,21 +281,27 @@ static NSString *LDAPContactInfoAttribute = nil; [response setStatus: 302]; [response setHeader: [container baseURLInContext: context] forKey: @"location"]; - auth = [[self clientObject] authenticatorInContext: context]; date = [NSCalendarDate calendarDate]; [date setTimeZone: [NSTimeZone timeZoneWithAbbreviation: @"GMT"]]; - cookie = [WOCookie cookieWithName: [auth cookieNameInContext: context] - value: @"discard"]; - [cookie setPath: @"/"]; - [cookie setExpires: [date yesterday]]; - [response addCookie: cookie]; + auth = [[self clientObject] authenticatorInContext: context]; + if ([auth respondsToSelector: @selector (cookieNameInContext:)]) + cookieName = [auth cookieNameInContext: context]; + else + cookieName = nil; + if (cookieName) + { + cookie = [WOCookie cookieWithName: cookieName value: @"discard"]; + [cookie setPath: @"/"]; + [cookie setExpires: [date yesterday]]; + [response addCookie: cookie]; + } [response setHeader: [date rfc822DateString] forKey: @"Last-Modified"]; - [response setHeader: @"no-store, no-cache, must-revalidate, max-age=0" - forKey: @"Cache-Control"]; - [response setHeader: @"post-check=0, pre-check=0" forKey: @"Cache-Control"]; + [response setHeader: @"no-store, no-cache, must-revalidate." + @" max-age=0, post-check=0, pre-check=0" + forKey: @"Cache-Control"]; [response setHeader: @"no-cache" forKey: @"Pragma"]; return response; diff --git a/UI/Templates/UIxPageFrame.wox b/UI/Templates/UIxPageFrame.wox index 25a266895..423d9ca37 100644 --- a/UI/Templates/UIxPageFrame.wox +++ b/UI/Templates/UIxPageFrame.wox @@ -46,8 +46,10 @@ >
- +