Monotone-Parent: 775b7e4fea80568033b5c8bf9b7c5220c7d36041
Monotone-Revision: ff12d30f9bf1800bb0b1bcbefba9493cdeeeccaf Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2010-01-28T21:42:03 Monotone-Branch: ca.inverse.sogomaint-2.0.2
parent
331e8a4f7d
commit
126651b6a7
46
ChangeLog
46
ChangeLog
|
@ -1,3 +1,49 @@
|
|||
2010-01-28 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/SOGo/SOGoSystemDefaults.m (-CASServiceURL)
|
||||
(-authenticationType): new accessors.
|
||||
|
||||
* SoObjects/SOGo/SOGoObject.m (+globallyUniqueObjectId): fixed to
|
||||
use the result of "random" rather than the function pointer.
|
||||
|
||||
* SoObjects/SOGo/SOGoCache.m (-setValue:forKey:expire)
|
||||
(-setValue:forKey:, -valueForKey:, -removeValueForKey:): new
|
||||
accessors.
|
||||
(-CASTicketFromIdentifier:, -CASSessionWithTicket:)
|
||||
(-setCASSession:withTicket:forIdentifier:): new accessors enabling
|
||||
the CAS session management.
|
||||
(-CASPGTIdFromPGTIOU:, -setCASPGTId:forPGTIOU:): new accessors
|
||||
enabling proxy ticket management (see casProxyAction below).
|
||||
|
||||
* UI/MainUI/SOGoUserHomePage.m (-logoffAction): we now pass the
|
||||
request application name as cookie path.
|
||||
|
||||
* UI/MainUI/SOGoRootPage.m (-casProxyAction): new method invoked
|
||||
by the CAS server when authenticating the SOGo server during a
|
||||
proxy request.
|
||||
(-_casDefaultAction): new "defaultAction" method executed in CAS mode.
|
||||
|
||||
* UI/Common/UIxPageFrame.m (-canLogoff): return NO in CAS mode.
|
||||
|
||||
* SoObjects/SOGo/SOGoWebAuthenticator.m (-checkLogin:password:)
|
||||
when authentication type is set to "cas", the password is the
|
||||
ticket identifying the CAS session. We therefore check if the
|
||||
login and the session login match.
|
||||
(-imapPasswordInContext:forServer:forceRenew:): new method that
|
||||
returns the "password" to use for IMAP connections. In CAS mode,
|
||||
a proxy ticket is fetched with the current CAS session. In
|
||||
standard mode, we still use the current user's password. The
|
||||
"forceRenew:" parameter enables the fetching of a new proxy ticket
|
||||
if the current one has expired.
|
||||
(-setupAuthFailResponse:withReason:inContext:): we know invoke
|
||||
"defaultAction" on the SOGoRootPage instance to make sure all the
|
||||
cookies and CAS tickets are taken into account.
|
||||
We now pass the request application name as cookie path.
|
||||
|
||||
* SoObjects/SOGo/SOGoCASSession.[hm]: new class module
|
||||
implementing the class in charge of CAS authentication and proxy
|
||||
transactions.
|
||||
|
||||
2010-01-27 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* Tools/SOGoToolBackup.m (-fetchUserIDs): retain allUsers to avoid
|
||||
|
|
|
@ -225,7 +225,7 @@ static NSString *sieveScriptName = @"sogo";
|
|||
SOGoUserDefaults *ud;
|
||||
SOGoDomainDefaults *dd;
|
||||
NGSieveClient *client;
|
||||
NSString *v;
|
||||
NSString *v, *password;
|
||||
BOOL b;
|
||||
|
||||
dd = [[context activeUser] domainDefaults];
|
||||
|
@ -319,11 +319,21 @@ static NSString *sieveScriptName = @"sogo";
|
|||
return NO;
|
||||
}
|
||||
|
||||
result = [client login: [[self imap4URL] user] password:[self imap4Password]];
|
||||
password = [self imap4PasswordRenewed: NO];
|
||||
if (!password) {
|
||||
[client closeConnection];
|
||||
return NO;
|
||||
}
|
||||
result = [client login: [[self imap4URL] user] password: password];
|
||||
if (![[result valueForKey:@"result"] boolValue]) {
|
||||
[self errorWithFormat: @"failure. Attempting with a renewed password."];
|
||||
password = [self imap4PasswordRenewed: NO];
|
||||
result = [client login: [[self imap4URL] user] password: password];
|
||||
}
|
||||
|
||||
if (![[result valueForKey:@"result"] boolValue]) {
|
||||
[self errorWithFormat: @"Could not login '%@' (%@) on Sieve server: %@: %@",
|
||||
[[self imap4URL] user], [self imap4Password], client, result];
|
||||
[[self imap4URL] user], password, client, result];
|
||||
[client closeConnection];
|
||||
return NO;
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
|
||||
- (NSURL *) imap4URL;
|
||||
- (NSString *) imap4Login;
|
||||
- (NSString *) imap4Password;
|
||||
- (NSString *) imap4PasswordRenewed: (BOOL) renew;
|
||||
|
||||
- (void) flushMailCaches;
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGExtensions/NSURL+misc.h>
|
||||
|
||||
#import <SoObjects/SOGo/SOGoAuthenticator.h>
|
||||
#import <SOGo/SOGoAuthenticator.h>
|
||||
|
||||
#import "SOGoMailManager.h"
|
||||
|
||||
|
@ -119,17 +119,36 @@ static BOOL debugOn = YES;
|
|||
|
||||
- (NGImap4Connection *) imap4Connection
|
||||
{
|
||||
NGImap4ConnectionManager *manager;
|
||||
NSString *password;
|
||||
|
||||
if (!imap4)
|
||||
{
|
||||
imap4 = [[self mailManager] connectionForURL: [self imap4URL]
|
||||
password: [self imap4Password]];
|
||||
if (imap4)
|
||||
[imap4 retain];
|
||||
else
|
||||
[self errorWithFormat:@"Could not connect IMAP4."];
|
||||
[self imap4URL];
|
||||
manager = [self mailManager];
|
||||
password = [self imap4PasswordRenewed: NO];
|
||||
if (password)
|
||||
{
|
||||
imap4 = [manager connectionForURL: imap4URL
|
||||
password: password];
|
||||
if (!imap4)
|
||||
{
|
||||
[self logWithFormat: @"renewing imap4 password"];
|
||||
password = [self imap4PasswordRenewed: YES];
|
||||
if (password)
|
||||
imap4 = [manager connectionForURL: imap4URL
|
||||
password: password];
|
||||
}
|
||||
}
|
||||
if (!imap4)
|
||||
{
|
||||
imap4 = (NGImap4Connection *) [NSNull null];
|
||||
[self errorWithFormat:@"Could not connect IMAP4"];
|
||||
}
|
||||
[imap4 retain];
|
||||
}
|
||||
|
||||
return imap4;
|
||||
return [imap4 isKindOfClass: [NSNull class]] ? nil : imap4;
|
||||
}
|
||||
|
||||
- (NSString *) relativeImap4Name
|
||||
|
@ -184,7 +203,7 @@ static BOOL debugOn = YES;
|
|||
return [container imap4Login];
|
||||
}
|
||||
|
||||
- (NSString *) imap4Password
|
||||
- (NSString *) imap4PasswordRenewed: (BOOL) renewed
|
||||
{
|
||||
/*
|
||||
Extract password from basic authentication.
|
||||
|
@ -193,8 +212,19 @@ static BOOL debugOn = YES;
|
|||
a) move the primary code to SOGoMailAccount
|
||||
b) cache the password
|
||||
*/
|
||||
NSURL *imapURL;
|
||||
NSString *password;
|
||||
|
||||
return [[self authenticatorInContext: context] passwordInContext: context];
|
||||
imapURL = [[self mailAccountFolder] imap4URL];
|
||||
|
||||
password = [[self authenticatorInContext: context]
|
||||
imapPasswordInContext: context
|
||||
forServer: [imapURL host]
|
||||
forceRenew: renewed];
|
||||
if (!password)
|
||||
[self errorWithFormat: @"no IMAP4 password available"];
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
- (NSMutableString *) traversalFromMailAccount
|
||||
|
|
|
@ -931,14 +931,35 @@ static BOOL debugSoParts = NO;
|
|||
doesn't tell us the new ID.
|
||||
*/
|
||||
NSURL *destImap4URL;
|
||||
|
||||
NGImap4ConnectionManager *manager;
|
||||
NSException *exc;
|
||||
NSString *password;
|
||||
|
||||
destImap4URL = ([_name length] == 0)
|
||||
? [[_target container] imap4URL]
|
||||
: [_target imap4URL];
|
||||
|
||||
return [[self mailManager] copyMailURL:[self imap4URL]
|
||||
toFolderURL:destImap4URL
|
||||
password:[self imap4Password]];
|
||||
|
||||
manager = [self mailManager];
|
||||
[self imap4URL];
|
||||
password = [self imap4PasswordRenewed: NO];
|
||||
if (password)
|
||||
{
|
||||
exc = [manager copyMailURL: imap4URL
|
||||
toFolderURL: destImap4URL
|
||||
password: password];
|
||||
if (exc)
|
||||
{
|
||||
[self
|
||||
logWithFormat: @"failure. Attempting with renewed imap4 password"];
|
||||
password = [self imap4PasswordRenewed: YES];
|
||||
if (password)
|
||||
exc = [manager copyMailURL: imap4URL
|
||||
toFolderURL: destImap4URL
|
||||
password: password];
|
||||
}
|
||||
}
|
||||
|
||||
return exc;
|
||||
}
|
||||
|
||||
/* actions */
|
||||
|
|
|
@ -46,6 +46,7 @@ SOGo_HEADER_FILES = \
|
|||
NSURL+DAV.h \
|
||||
\
|
||||
SOGoAuthenticator.h \
|
||||
SOGoCASSession.h \
|
||||
SOGoDAVAuthenticator.h \
|
||||
SOGoProxyAuthenticator.h \
|
||||
SOGoWebAuthenticator.h \
|
||||
|
@ -105,8 +106,9 @@ SOGo_OBJC_FILES = \
|
|||
NSString+Utilities.m \
|
||||
NSURL+DAV.m \
|
||||
\
|
||||
SOGoCASSession.m \
|
||||
SOGoDAVAuthenticator.m \
|
||||
SOGoProxyAuthenticator.m \
|
||||
SOGoProxyAuthenticator.m \
|
||||
SOGoWebAuthenticator.m \
|
||||
SOGoWebDAVAclManager.m \
|
||||
SOGoWebDAVValue.m \
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
|
||||
- (NSString *) passwordInContext: (WOContext *) context;
|
||||
- (SOGoUser *) userInContext: (WOContext *) context;
|
||||
- (NSString *) imapPasswordInContext: (WOContext *) context
|
||||
forServer: (NSString *) imapServer
|
||||
forceRenew: (BOOL) renew;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/* SOGoCASSession.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
* 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 SOGOCASSESSION_H
|
||||
#define SOGOCASSESSION_H
|
||||
|
||||
/* implementation of the CAS protocol as required for a client/proxy:
|
||||
http://www.jasig.org/cas/protocol */
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class NSString;
|
||||
@class NSMutableDictionary;
|
||||
|
||||
@interface SOGoCASSession : NSObject
|
||||
{
|
||||
NSString *ticket;
|
||||
NSString *login;
|
||||
NSString *pgt;
|
||||
NSString *identifier;
|
||||
NSMutableDictionary *proxyTickets;
|
||||
BOOL cacheUpdateNeeded;
|
||||
NSString *currentProxyService;
|
||||
}
|
||||
|
||||
+ (NSString *) CASURLWithAction: (NSString *) casAction
|
||||
andParameters: (NSDictionary *) parameters;
|
||||
|
||||
+ (SOGoCASSession *) CASSessionWithTicket: (NSString *) newTicket;
|
||||
+ (SOGoCASSession *) CASSessionWithIdentifier: (NSString *) identifier;
|
||||
|
||||
- (NSString *) identifier;
|
||||
|
||||
- (void) setTicket: (NSString *) newTicket;
|
||||
- (NSString *) ticket;
|
||||
|
||||
- (NSString *) login;
|
||||
- (NSString *) identifier;
|
||||
|
||||
- (NSString *) ticketForService: (NSString *) service;
|
||||
- (void) invalidateTicketForService: (NSString *) service;
|
||||
|
||||
- (void) updateCache;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOCASSESSION_H */
|
|
@ -0,0 +1,454 @@
|
|||
/* SOGoCASSession.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
* 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 <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
|
||||
#import <DOM/DOMElement.h>
|
||||
#import <DOM/DOMDocument.h>
|
||||
#import <DOM/DOMProtocols.h>
|
||||
#import <DOM/DOMText.h>
|
||||
|
||||
#import <NGObjWeb/WOApplication.h>
|
||||
#import <NGObjWeb/WOHTTPConnection.h>
|
||||
#import <NGObjWeb/WORequest.h>
|
||||
#import <NGObjWeb/WOContext.h>
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import "NSDictionary+BSJSONAdditions.h"
|
||||
#import "NSString+Utilities.h"
|
||||
#import "SOGoCache.h"
|
||||
#import "SOGoObject.h"
|
||||
#import "SOGoSystemDefaults.h"
|
||||
|
||||
#import "SOGoCASSession.h"
|
||||
|
||||
@implementation SOGoCASSession
|
||||
|
||||
+ (NSString *) CASURLWithAction: (NSString *) casAction
|
||||
andParameters: (NSDictionary *) parameters
|
||||
{
|
||||
NSString *casActionURL, *baseCASURL;
|
||||
SOGoSystemDefaults *sd;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
baseCASURL = [sd CASServiceURL];
|
||||
if ([baseCASURL length])
|
||||
casActionURL = [baseCASURL composeURLWithAction: casAction
|
||||
parameters: parameters
|
||||
andHash: NO];
|
||||
else
|
||||
{
|
||||
[self errorWithFormat:
|
||||
@"'SOGoCASServiceURL' is empty in the user defaults"];
|
||||
casActionURL = nil;
|
||||
}
|
||||
|
||||
return casActionURL;
|
||||
}
|
||||
|
||||
+ (SOGoCASSession *) CASSessionWithTicket: (NSString *) newTicket
|
||||
{
|
||||
SOGoCASSession *newSession;
|
||||
|
||||
if (newTicket)
|
||||
{
|
||||
newSession = [self new];
|
||||
[newSession autorelease];
|
||||
[newSession setTicket: newTicket];
|
||||
}
|
||||
else
|
||||
newSession = nil;
|
||||
|
||||
return newSession;
|
||||
}
|
||||
|
||||
+ (SOGoCASSession *) CASSessionWithIdentifier: (NSString *) identifier
|
||||
{
|
||||
SOGoCASSession *session;
|
||||
SOGoCache *cache;
|
||||
NSString *ticket;
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
ticket = [cache CASTicketFromIdentifier: identifier];
|
||||
session = [self CASSessionWithTicket: ticket];
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
ticket = nil;
|
||||
login = nil;
|
||||
pgt = nil;
|
||||
identifier = nil;
|
||||
proxyTickets = nil;
|
||||
cacheUpdateNeeded = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[login release];
|
||||
[pgt release];
|
||||
[ticket release];
|
||||
[proxyTickets release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) _loadSessionFromCache
|
||||
{
|
||||
SOGoCache *cache;
|
||||
NSString *jsonSession;
|
||||
NSDictionary *sessionDict;
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
jsonSession = [cache CASSessionWithTicket: ticket];
|
||||
if ([jsonSession length])
|
||||
{
|
||||
sessionDict = [NSMutableDictionary dictionaryWithJSONString: jsonSession];
|
||||
ASSIGN (login, [sessionDict objectForKey: @"login"]);
|
||||
ASSIGN (pgt, [sessionDict objectForKey: @"pgt"]);
|
||||
ASSIGN (identifier, [sessionDict objectForKey: @"identifier"]);
|
||||
ASSIGN (proxyTickets, [sessionDict objectForKey: @"proxyTickets"]);
|
||||
if (!proxyTickets)
|
||||
proxyTickets = [NSMutableDictionary new];
|
||||
}
|
||||
else
|
||||
cacheUpdateNeeded = YES;
|
||||
}
|
||||
|
||||
- (void) _saveSessionToCache
|
||||
{
|
||||
SOGoCache *cache;
|
||||
NSString *jsonSession;
|
||||
NSMutableDictionary *sessionDict;
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
sessionDict = [NSMutableDictionary dictionary];
|
||||
[sessionDict setObject: login forKey: @"login"];
|
||||
if (pgt)
|
||||
[sessionDict setObject: pgt forKey: @"pgt"];
|
||||
[sessionDict setObject: identifier forKey: @"identifier"];
|
||||
if ([proxyTickets count])
|
||||
[sessionDict setObject: proxyTickets forKey: @"proxyTickets"];
|
||||
jsonSession = [sessionDict jsonStringValue];
|
||||
[cache setCASSession: jsonSession
|
||||
withTicket: ticket
|
||||
forIdentifier: identifier];
|
||||
}
|
||||
|
||||
- (void) setTicket: (NSString *) newTicket
|
||||
{
|
||||
ASSIGN (ticket, newTicket);
|
||||
[self _loadSessionFromCache];
|
||||
}
|
||||
|
||||
- (NSString *) ticket
|
||||
{
|
||||
return ticket;
|
||||
}
|
||||
|
||||
- (void) _parseSuccessElement: (NGDOMElement *) element
|
||||
{
|
||||
NSString *tagName, *pgtIou;
|
||||
NGDOMText *valueNode;
|
||||
SOGoCache *cache;
|
||||
|
||||
tagName = [element tagName];
|
||||
valueNode = (NGDOMText *) [element firstChild];
|
||||
if ([valueNode nodeType] == DOM_TEXT_NODE)
|
||||
{
|
||||
if ([tagName isEqualToString: @"user"])
|
||||
ASSIGN (login, [valueNode nodeValue]);
|
||||
else if ([tagName isEqualToString: @"proxyGrantingTicket"])
|
||||
{
|
||||
pgtIou = [valueNode nodeValue];
|
||||
cache = [SOGoCache sharedCache];
|
||||
ASSIGN (pgt, [cache CASPGTIdFromPGTIOU: pgtIou]);
|
||||
}
|
||||
else
|
||||
[self logWithFormat: @"unhandled success tag '%@'", tagName];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _parseProxySuccessElement: (NGDOMElement *) element
|
||||
{
|
||||
NSString *tagName;
|
||||
NGDOMText *valueNode;
|
||||
|
||||
tagName = [element tagName];
|
||||
if ([tagName isEqualToString: @"proxyTicket"])
|
||||
{
|
||||
valueNode = (NGDOMText *) [element firstChild];
|
||||
if ([valueNode nodeType] == DOM_TEXT_NODE)
|
||||
{
|
||||
[proxyTickets setObject: [valueNode nodeValue]
|
||||
forKey: currentProxyService];
|
||||
cacheUpdateNeeded = YES;
|
||||
}
|
||||
}
|
||||
else
|
||||
[self logWithFormat: @"unhandled proxy success tag '%@'", tagName];
|
||||
}
|
||||
|
||||
- (void) _parseProxyFailureElement: (NGDOMElement *) element
|
||||
{
|
||||
NSMutableString *errorString;
|
||||
NSString *errorText;
|
||||
NGDOMText *valueNode;
|
||||
|
||||
errorString = [NSMutableString stringWithString: (@"a CAS failure occured"
|
||||
@" during operation")];
|
||||
if ([element hasAttribute: @"code"])
|
||||
[errorString appendFormat: @" (code: '%@')",
|
||||
[element attribute: @"code"]];
|
||||
valueNode = (NGDOMText *) [element firstChild];
|
||||
if (valueNode)
|
||||
{
|
||||
[errorString appendString: @":"];
|
||||
while (valueNode)
|
||||
{
|
||||
if ([valueNode nodeType] == DOM_TEXT_NODE)
|
||||
{
|
||||
errorText = [[valueNode nodeValue] stringByTrimmingSpaces];
|
||||
[errorString appendFormat: @" %@", errorText];
|
||||
}
|
||||
valueNode = (NGDOMText *) [valueNode nextSibling];
|
||||
}
|
||||
}
|
||||
|
||||
[self logWithFormat: errorString];
|
||||
}
|
||||
|
||||
- (SEL) _selectorForSubElementsOfTag: (NSString *) tag
|
||||
{
|
||||
static NSMutableDictionary *mapping = nil;
|
||||
NSString *methodName;
|
||||
SEL selector;
|
||||
|
||||
if (!mapping)
|
||||
{
|
||||
mapping = [NSMutableDictionary new];
|
||||
[mapping setObject: @"_parseSuccessElement:"
|
||||
forKey: @"authenticationSuccess"];
|
||||
[mapping setObject: @"_parseProxySuccessElement:"
|
||||
forKey: @"proxySuccess"];
|
||||
}
|
||||
|
||||
methodName = [mapping objectForKey: tag];
|
||||
if (methodName)
|
||||
selector = NSSelectorFromString (methodName);
|
||||
else
|
||||
{
|
||||
selector = NULL;
|
||||
[self errorWithFormat: @"unhandled response tag '%@'", tag];
|
||||
}
|
||||
|
||||
return selector;
|
||||
}
|
||||
|
||||
- (void) _parseResponseElement: (NGDOMElement *) element
|
||||
{
|
||||
id <DOMNodeList> nodes;
|
||||
NGDOMElement *currentNode;
|
||||
SEL parseElementSelector;
|
||||
NSString *tagName;
|
||||
int count, max;
|
||||
|
||||
tagName = [element tagName];
|
||||
if ([tagName isEqualToString: @"proxyFailure"])
|
||||
[self _parseProxyFailureElement: element];
|
||||
else
|
||||
{
|
||||
parseElementSelector = [self _selectorForSubElementsOfTag: tagName];
|
||||
if (parseElementSelector)
|
||||
{
|
||||
nodes = [element childNodes];
|
||||
max = [nodes length];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
currentNode = [nodes objectAtIndex: count];
|
||||
if ([currentNode nodeType] == DOM_ELEMENT_NODE)
|
||||
[self performSelector: parseElementSelector
|
||||
withObject: currentNode];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _parseDOMResponse: (NGDOMDocument *) response
|
||||
{
|
||||
NGDOMElement *top;
|
||||
id <DOMNodeList> nodes;
|
||||
NGDOMElement *currentNode;
|
||||
int count, max;
|
||||
|
||||
top = [response documentElement];
|
||||
nodes = [top childNodes];
|
||||
max = [nodes length];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
currentNode = [nodes objectAtIndex: count];
|
||||
if ([currentNode nodeType] == DOM_ELEMENT_NODE)
|
||||
[self _parseResponseElement: currentNode];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _performCASRequestWithAction: (NSString *) casAction
|
||||
andParameters: (NSDictionary *) parameters
|
||||
{
|
||||
NSString *requestURL;
|
||||
NSURL *url;
|
||||
WORequest *request;
|
||||
WOResponse *response;
|
||||
WOHTTPConnection *httpConnection;
|
||||
|
||||
requestURL = [[self class] CASURLWithAction: casAction
|
||||
andParameters: parameters];
|
||||
if (requestURL)
|
||||
{
|
||||
url = [NSURL URLWithString: requestURL];
|
||||
httpConnection = [[WOHTTPConnection alloc]
|
||||
initWithURL: url];
|
||||
[httpConnection autorelease];
|
||||
request = [[WORequest alloc] initWithMethod: @"GET"
|
||||
uri: [requestURL hostlessURL]
|
||||
httpVersion: @"HTTP/1.1"
|
||||
headers: nil content: nil
|
||||
userInfo: nil];
|
||||
[request autorelease];
|
||||
[httpConnection sendRequest: request];
|
||||
response = [httpConnection readResponse];
|
||||
[self _parseDOMResponse: [response contentAsDOMDocument]];
|
||||
}
|
||||
}
|
||||
|
||||
/* returns the URL that matches -[SOGoRootPage casProxyAction] */
|
||||
- (NSString *) _pgtUrlFromURL: (NSURL *) soURL
|
||||
{
|
||||
WOApplication *application;
|
||||
NSString *pgtURL;
|
||||
WORequest *request;
|
||||
|
||||
application = [WOApplication application];
|
||||
request = [[application context] request];
|
||||
pgtURL = [NSString stringWithFormat:
|
||||
@"https://%@/%@/casProxy",
|
||||
[soURL host], [request applicationName]];
|
||||
|
||||
return pgtURL;
|
||||
}
|
||||
|
||||
- (void) _fetchTicketData
|
||||
{
|
||||
NSDictionary *params;
|
||||
NSURL *soURL;
|
||||
NSString *serviceURL;
|
||||
|
||||
soURL = [[WOApplication application] soURL];
|
||||
serviceURL = [soURL absoluteString];
|
||||
|
||||
params = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
ticket, @"ticket", serviceURL, @"service",
|
||||
[self _pgtUrlFromURL: soURL], @"pgtUrl",
|
||||
nil];
|
||||
[self _performCASRequestWithAction: @"serviceValidate"
|
||||
andParameters: params];
|
||||
identifier = [SOGoObject globallyUniqueObjectId];
|
||||
[identifier retain];
|
||||
|
||||
cacheUpdateNeeded = YES;
|
||||
}
|
||||
|
||||
- (NSString *) login
|
||||
{
|
||||
if (!login)
|
||||
[self _fetchTicketData];
|
||||
|
||||
return login;
|
||||
}
|
||||
|
||||
- (NSString *) identifier
|
||||
{
|
||||
return identifier;
|
||||
}
|
||||
|
||||
- (void) updateCache
|
||||
{
|
||||
if (cacheUpdateNeeded)
|
||||
{
|
||||
[self _saveSessionToCache];
|
||||
cacheUpdateNeeded = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _fetchTicketDataForService: (NSString *) service
|
||||
{
|
||||
NSDictionary *params;
|
||||
|
||||
params = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
pgt, @"pgt", service, @"targetService",
|
||||
nil];
|
||||
[self _performCASRequestWithAction: @"proxy"
|
||||
andParameters: params];
|
||||
}
|
||||
|
||||
- (NSString *) ticketForService: (NSString *) service
|
||||
{
|
||||
NSString *proxyTicket;
|
||||
|
||||
if (pgt)
|
||||
{
|
||||
proxyTicket = [proxyTickets objectForKey: service];
|
||||
if (!proxyTicket)
|
||||
{
|
||||
currentProxyService = service;
|
||||
[self _fetchTicketDataForService: service];
|
||||
proxyTicket = [proxyTickets objectForKey: service];
|
||||
if (proxyTicket)
|
||||
cacheUpdateNeeded = YES;
|
||||
currentProxyService = nil;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"attempted to obtain a ticket for service '%@'"
|
||||
@" while no PGT available", service];
|
||||
proxyTicket = nil;
|
||||
}
|
||||
|
||||
return proxyTicket;
|
||||
}
|
||||
|
||||
- (void) invalidateTicketForService: (NSString *) service
|
||||
{
|
||||
[proxyTickets removeObjectForKey: service];
|
||||
cacheUpdateNeeded = YES;
|
||||
}
|
||||
|
||||
@end
|
|
@ -62,6 +62,11 @@
|
|||
withName: (NSString *) userName;
|
||||
- (id) userNamed: (NSString *) name;
|
||||
|
||||
/* NSDictionary-like methods */
|
||||
- (void) setValue: (NSString *) value forKey: (NSString *) key;
|
||||
- (NSString *) valueForKey: (NSString *) key;
|
||||
- (void) removeValueForKey: (NSString *) key;
|
||||
|
||||
- (void) setUserAttributes: (NSString *) attributes
|
||||
forLogin: (NSString *) login;
|
||||
- (NSString *) userAttributesForLogin: (NSString *) theLogin;
|
||||
|
@ -74,6 +79,17 @@
|
|||
forLogin: (NSString *) login;
|
||||
- (NSString *) userSettingsForLogin: (NSString *) theLogin;
|
||||
|
||||
/* CAS support */
|
||||
- (NSString *) CASTicketFromIdentifier: (NSString *) identifier;
|
||||
- (NSString *) CASSessionWithTicket: (NSString *) ticket;
|
||||
- (void) setCASSession: (NSString *) casSession
|
||||
withTicket: (NSString *) ticket
|
||||
forIdentifier: (NSString *) identifier;
|
||||
|
||||
- (NSString *) CASPGTIdFromPGTIOU: (NSString *) pgtIou;
|
||||
- (void) setCASPGTId: (NSString *) pgtId
|
||||
forPGTIOU: (NSString *) pgtIou;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOCACHE_H */
|
||||
|
|
|
@ -119,8 +119,8 @@
|
|||
|
||||
- (void) dealloc
|
||||
{
|
||||
memcached_server_free(servers);
|
||||
memcached_free(handle);
|
||||
memcached_server_free (servers);
|
||||
memcached_free (handle);
|
||||
[memcachedServerName release];
|
||||
[cache release];
|
||||
[users release];
|
||||
|
@ -203,73 +203,124 @@
|
|||
// For non-blocking cache method, see memcached_behavior_set and MEMCACHED_BEHAVIOR_NO_BLOCK
|
||||
// memcached is thread-safe so no need to lock here.
|
||||
//
|
||||
- (void) _cacheValues: (NSString *) theAttributes
|
||||
ofType: (NSString *) theType
|
||||
forLogin: (NSString *) theLogin
|
||||
- (void) setValue: (NSString *) value
|
||||
forKey: (NSString *) key
|
||||
expire: (float) expiration
|
||||
{
|
||||
NSData *keyData, *valueData;
|
||||
memcached_return error;
|
||||
NSString *keyName;
|
||||
NSData *key, *value;
|
||||
|
||||
keyName = [NSString stringWithFormat: @"%@+%@", theLogin, theType];
|
||||
if (handle)
|
||||
{
|
||||
key = [keyName dataUsingEncoding: NSUTF8StringEncoding];
|
||||
value = [theAttributes dataUsingEncoding: NSUTF8StringEncoding];
|
||||
error = memcached_set(handle,
|
||||
[key bytes], [key length],
|
||||
[value bytes], [value length],
|
||||
cleanupInterval, 0);
|
||||
[localCache setObject: theAttributes forKey: keyName];
|
||||
|
||||
keyData = [key dataUsingEncoding: NSUTF8StringEncoding];
|
||||
valueData = [value dataUsingEncoding: NSUTF8StringEncoding];
|
||||
error = memcached_set (handle,
|
||||
[keyData bytes], [keyData length],
|
||||
[valueData bytes], [valueData length],
|
||||
expiration, 0);
|
||||
if (error != MEMCACHED_SUCCESS)
|
||||
[self logWithFormat: @"memcached error: unable to cache values with subtype '%@' for user '%@'", theType, theLogin];
|
||||
[self logWithFormat:
|
||||
@"memcached error: unable to cache values for key '%@'",
|
||||
key];
|
||||
//else
|
||||
//[self logWithFormat: @"memcached: cached values (%s) with subtype %@
|
||||
//for user %@", value, theType, theLogin];
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"attempting to cache value for key '%@' while"
|
||||
" no handle exists", keyName];
|
||||
[self errorWithFormat: (@"attempting to cache value for key '%@' while"
|
||||
" no handle exists"), key];
|
||||
}
|
||||
|
||||
- (void) setValue: (NSString *) value
|
||||
forKey: (NSString *) key
|
||||
{
|
||||
[self setValue: value forKey: key
|
||||
expire: cleanupInterval];
|
||||
}
|
||||
|
||||
- (NSString *) valueForKey: (NSString *) key
|
||||
{
|
||||
NSString *valueString;
|
||||
NSData *keyData;
|
||||
char *value;
|
||||
size_t vlen;
|
||||
memcached_return rc;
|
||||
unsigned int flags;
|
||||
|
||||
if (handle)
|
||||
{
|
||||
keyData = [key dataUsingEncoding: NSUTF8StringEncoding];
|
||||
value = memcached_get (handle, [keyData bytes], [keyData length],
|
||||
&vlen, &flags, &rc);
|
||||
if (rc == MEMCACHED_SUCCESS && value)
|
||||
{
|
||||
valueString
|
||||
= [[NSString alloc] initWithBytesNoCopy: value
|
||||
length: vlen
|
||||
encoding: NSUTF8StringEncoding
|
||||
freeWhenDone: YES];
|
||||
[valueString autorelease];
|
||||
}
|
||||
else
|
||||
valueString = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
valueString = nil;
|
||||
[self errorWithFormat: @"attempting to retrieved cached value for key"
|
||||
@" '%@' while no handle exists", key];
|
||||
}
|
||||
|
||||
return valueString;
|
||||
}
|
||||
|
||||
- (void) removeValueForKey: (NSString *) key
|
||||
{
|
||||
NSData *keyData;
|
||||
memcached_return rc;
|
||||
|
||||
if (handle)
|
||||
{
|
||||
keyData = [key dataUsingEncoding: NSUTF8StringEncoding];
|
||||
rc = memcached_delete (handle, [keyData bytes], [keyData length],
|
||||
0);
|
||||
if (rc != MEMCACHED_SUCCESS)
|
||||
[self errorWithFormat: (@"failure deleting cached value for key"
|
||||
@" '%@'"),
|
||||
key];
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: (@"attempting to delete cached value for key"
|
||||
@" '%@' while no handle exists"),
|
||||
key];
|
||||
}
|
||||
|
||||
- (void) _cacheValues: (NSString *) theAttributes
|
||||
ofType: (NSString *) theType
|
||||
forLogin: (NSString *) theLogin
|
||||
{
|
||||
NSString *keyName;
|
||||
|
||||
keyName = [NSString stringWithFormat: @"%@+%@", theLogin, theType];
|
||||
[self setValue: theAttributes forKey: keyName];
|
||||
[localCache setObject: theAttributes forKey: keyName];
|
||||
}
|
||||
|
||||
- (NSString *) _valuesOfType: (NSString *) theType
|
||||
forLogin: (NSString *) theLogin
|
||||
{
|
||||
NSString *valueString, *keyName;
|
||||
NSData *key;
|
||||
char *value;
|
||||
size_t vlen;
|
||||
memcached_return rc;
|
||||
unsigned int flags;
|
||||
|
||||
valueString = nil;
|
||||
|
||||
keyName = [NSString stringWithFormat: @"%@+%@", theLogin, theType];
|
||||
if (handle)
|
||||
valueString = [localCache objectForKey: keyName];
|
||||
if (!valueString)
|
||||
{
|
||||
valueString = [localCache objectForKey: keyName];
|
||||
if (!valueString)
|
||||
{
|
||||
key = [keyName dataUsingEncoding: NSUTF8StringEncoding];
|
||||
value = memcached_get (handle, [key bytes], [key length],
|
||||
&vlen, &flags, &rc);
|
||||
if (rc == MEMCACHED_SUCCESS && value)
|
||||
{
|
||||
valueString
|
||||
= [[NSString alloc] initWithBytesNoCopy: value
|
||||
length: vlen
|
||||
encoding: NSUTF8StringEncoding
|
||||
freeWhenDone: YES];
|
||||
[valueString autorelease];
|
||||
// Cache the value in our localCache
|
||||
[localCache setObject: valueString forKey: keyName];
|
||||
}
|
||||
}
|
||||
valueString = [self valueForKey: keyName];
|
||||
if (valueString)
|
||||
[localCache setObject: valueString forKey: keyName];
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"attempting to retrieved cached value for key"
|
||||
@" '%@' while no handle exists", keyName];
|
||||
|
||||
return valueString;
|
||||
}
|
||||
|
@ -310,4 +361,47 @@
|
|||
return [self _valuesOfType: @"settings" forLogin: theLogin];
|
||||
}
|
||||
|
||||
/* CAS session support */
|
||||
- (NSString *) CASTicketFromIdentifier: (NSString *) identifier
|
||||
{
|
||||
return [self valueForKey: [NSString stringWithFormat: @"cas-id:%@",
|
||||
identifier]];
|
||||
}
|
||||
|
||||
- (NSString *) CASSessionWithTicket: (NSString *) ticket
|
||||
{
|
||||
return [self valueForKey: [NSString stringWithFormat: @"cas-ticket:%@",
|
||||
ticket]];
|
||||
}
|
||||
|
||||
- (void) setCASSession: (NSString *) casSession
|
||||
withTicket: (NSString *) ticket
|
||||
forIdentifier: (NSString *) identifier
|
||||
{
|
||||
[self setValue: ticket
|
||||
forKey: [NSString stringWithFormat: @"cas-id:%@", identifier]];
|
||||
[self setValue: casSession
|
||||
forKey: [NSString stringWithFormat: @"cas-ticket:%@", ticket]];
|
||||
}
|
||||
|
||||
- (NSString *) CASPGTIdFromPGTIOU: (NSString *) pgtIou
|
||||
{
|
||||
NSString *casPgtId, *key;
|
||||
|
||||
key = [NSString stringWithFormat: @"cas-pgtiou:%@", pgtIou];
|
||||
casPgtId = [self valueForKey: key];
|
||||
/* we directly remove the value as it can only be used once anyway */
|
||||
if (casPgtId)
|
||||
[self removeValueForKey: key];
|
||||
|
||||
return casPgtId;
|
||||
}
|
||||
|
||||
- (void) setCASPGTId: (NSString *) pgtId
|
||||
forPGTIOU: (NSString *) pgtIou
|
||||
{
|
||||
[self setValue: pgtId
|
||||
forKey: [NSString stringWithFormat: @"cas-pgtiou:%@", pgtIou]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -69,6 +69,13 @@
|
|||
return password;
|
||||
}
|
||||
|
||||
- (NSString *) imapPasswordInContext: (WOContext *) context
|
||||
forServer: (NSString *) imapServer
|
||||
forceRenew: (BOOL) renew
|
||||
{
|
||||
return [self passwordInContext: context];
|
||||
}
|
||||
|
||||
/* create SOGoUser */
|
||||
|
||||
- (SOGoUser *) userInContext: (WOContext *)_ctx
|
||||
|
|
|
@ -129,7 +129,7 @@
|
|||
f = [[NSDate date] timeIntervalSince1970];
|
||||
|
||||
return [NSString stringWithFormat:@"%0X-%0X-%0X-%0X",
|
||||
pid, (int) f, sequence++, random];
|
||||
pid, (int) f, sequence++, (int) rndm];
|
||||
}
|
||||
|
||||
- (NSString *) globallyUniqueObjectId
|
||||
|
|
|
@ -60,6 +60,10 @@
|
|||
- (NSArray *) supportedLanguages;
|
||||
- (NSString *) loginSuffix;
|
||||
|
||||
- (NSString *) authenticationType;
|
||||
|
||||
- (NSString *) CASServiceURL;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SOGOSYSTEMDEFAULTS_H */
|
||||
|
|
|
@ -285,4 +285,14 @@ BootstrapNSUserDefaults ()
|
|||
return [self stringForKey: @"SOGoLoginSuffix"];
|
||||
}
|
||||
|
||||
- (NSString *) authenticationType
|
||||
{
|
||||
return [[self stringForKey: @"SOGoAuthenticationType"] lowercaseString];
|
||||
}
|
||||
|
||||
- (NSString *) CASServiceURL
|
||||
{
|
||||
return [self stringForKey: @"SOGoCASServiceURL"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -36,9 +36,11 @@
|
|||
|
||||
#import <MainUI/SOGoRootPage.h>
|
||||
|
||||
#import "SOGoUserManager.h"
|
||||
#import "SOGoCASSession.h"
|
||||
#import "SOGoPermissions.h"
|
||||
#import "SOGoSystemDefaults.h"
|
||||
#import "SOGoUser.h"
|
||||
#import "SOGoUserManager.h"
|
||||
|
||||
#import "SOGoWebAuthenticator.h"
|
||||
|
||||
|
@ -57,8 +59,24 @@
|
|||
- (BOOL) checkLogin: (NSString *) _login
|
||||
password: (NSString *) _pwd
|
||||
{
|
||||
return [[SOGoUserManager sharedUserManager] checkLogin: _login
|
||||
SOGoSystemDefaults *sd;
|
||||
BOOL rc;
|
||||
SOGoCASSession *session;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
if ([[sd authenticationType] isEqualToString: @"cas"])
|
||||
{
|
||||
session = [SOGoCASSession CASSessionWithIdentifier: _pwd];
|
||||
if (session)
|
||||
rc = [[session login] isEqualToString: _login];
|
||||
else
|
||||
rc = NO;
|
||||
}
|
||||
else
|
||||
rc = [[SOGoUserManager sharedUserManager] checkLogin: _login
|
||||
andPassword: _pwd];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (SOGoUser *) userInContext: (WOContext *)_ctx
|
||||
|
@ -83,7 +101,7 @@
|
|||
{
|
||||
NSArray *creds;
|
||||
NSString *auth, *password;
|
||||
|
||||
|
||||
auth = [[context request]
|
||||
cookieValueForKey: [self cookieNameInContext: context]];
|
||||
creds = [self parseCredentials: auth];
|
||||
|
@ -95,6 +113,33 @@
|
|||
return password;
|
||||
}
|
||||
|
||||
- (NSString *) imapPasswordInContext: (WOContext *) context
|
||||
forServer: (NSString *) imapServer
|
||||
forceRenew: (BOOL) renew
|
||||
{
|
||||
SOGoSystemDefaults *sd;
|
||||
SOGoCASSession *session;
|
||||
NSString *password, *service;
|
||||
|
||||
password = [self passwordInContext: context];
|
||||
if ([password length])
|
||||
{
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
if ([[sd authenticationType] isEqualToString: @"cas"])
|
||||
{
|
||||
session = [SOGoCASSession CASSessionWithIdentifier: password];
|
||||
service = [NSString stringWithFormat: @"imap://%@", imapServer];
|
||||
if (renew)
|
||||
[session invalidateTicketForService: service];
|
||||
password = [session ticketForService: service];
|
||||
if ([password length] || renew)
|
||||
[session updateCache];
|
||||
}
|
||||
}
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
/* create SOGoUser */
|
||||
|
||||
- (SOGoUser *) userWithLogin: (NSString *) login
|
||||
|
@ -113,7 +158,7 @@
|
|||
*/
|
||||
WOResponse *response;
|
||||
NSString *auth;
|
||||
|
||||
|
||||
auth = [[context request]
|
||||
cookieValueForKey: [self cookieNameInContext:context]];
|
||||
if ([auth isEqualToString: @"discard"])
|
||||
|
@ -133,16 +178,20 @@
|
|||
inContext: (WOContext *) context
|
||||
{
|
||||
WOComponent *page;
|
||||
WORequest *request;
|
||||
WOCookie *authCookie;
|
||||
NSCalendarDate *date;
|
||||
NSString *appName;
|
||||
|
||||
request = [context request];
|
||||
page = [[WOApplication application] pageWithName: @"SOGoRootPage"
|
||||
forRequest: [context request]];
|
||||
[[SoDefaultRenderer sharedRenderer] renderObject: page
|
||||
forRequest: request];
|
||||
[[SoDefaultRenderer sharedRenderer] renderObject: [page defaultAction]
|
||||
inContext: context];
|
||||
authCookie = [WOCookie cookieWithName: [self cookieNameInContext: context]
|
||||
value: @"discard"];
|
||||
[authCookie setPath: @"/"];
|
||||
appName = [request applicationName];
|
||||
[authCookie setPath: [NSString stringWithFormat: @"/%@/", appName]];
|
||||
date = [NSCalendarDate calendarDate];
|
||||
[authCookie setExpires: [date yesterday]];
|
||||
[response addCookie: authCookie];
|
||||
|
|
|
@ -409,10 +409,15 @@
|
|||
{
|
||||
BOOL canLogoff;
|
||||
id auth;
|
||||
SOGoSystemDefaults *sd;
|
||||
|
||||
auth = [[self clientObject] authenticatorInContext: context];
|
||||
if ([auth respondsToSelector: @selector (cookieNameInContext:)])
|
||||
canLogoff = ([[auth cookieNameInContext: context] length] > 0);
|
||||
{
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
canLogoff = ([[auth cookieNameInContext: context] length] > 0
|
||||
&& ![[sd authenticationType] isEqualToString: @"cas"]);
|
||||
}
|
||||
else
|
||||
canLogoff = NO;
|
||||
|
||||
|
|
|
@ -19,8 +19,11 @@
|
|||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSException.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
#import <NGObjWeb/WOApplication.h>
|
||||
#import <NGObjWeb/WOContext.h>
|
||||
#import <NGObjWeb/WOCookie.h>
|
||||
|
@ -32,6 +35,9 @@
|
|||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
#import <SOGo/SOGoCache.h>
|
||||
#import <SOGo/SOGoCASSession.h>
|
||||
#import <SOGo/SOGoDomainDefaults.h>
|
||||
#import <SOGo/SOGoSystemDefaults.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
|
@ -54,6 +60,27 @@
|
|||
return [NSString stringWithFormat: @"%@/connect", [self applicationPath]];
|
||||
}
|
||||
|
||||
- (WOCookie *) _cookieWithUsername: (NSString *) username
|
||||
andPassword: (NSString *) password
|
||||
forAuthenticator: (SOGoWebAuthenticator *) auth
|
||||
{
|
||||
WOCookie *authCookie;
|
||||
NSString *cookieValue, *cookieString, *appName;
|
||||
|
||||
cookieString = [NSString stringWithFormat: @"%@:%@",
|
||||
username, password];
|
||||
cookieValue = [NSString stringWithFormat: @"basic %@",
|
||||
[cookieString stringByEncodingBase64]];
|
||||
authCookie = [WOCookie cookieWithName: [auth cookieNameInContext: context]
|
||||
value: cookieValue];
|
||||
appName = [[context request] applicationName];
|
||||
[authCookie setPath: [NSString stringWithFormat: @"/%@/", appName]];
|
||||
/* enable this when we have code to determine whether request is HTTPS:
|
||||
[authCookie setIsSecure: YES]; */
|
||||
|
||||
return authCookie;
|
||||
}
|
||||
|
||||
/* actions */
|
||||
- (id <WOActionResults>) connectAction
|
||||
{
|
||||
|
@ -62,70 +89,154 @@
|
|||
WOCookie *authCookie;
|
||||
SOGoWebAuthenticator *auth;
|
||||
SOGoUserDefaults *ud;
|
||||
NSString *cookieValue, *cookieString;
|
||||
NSString *userName, *password, *language;
|
||||
NSString *username, *password, *language;
|
||||
NSArray *supportedLanguages;
|
||||
|
||||
auth = [[WOApplication application]
|
||||
authenticatorInContext: context];
|
||||
request = [context request];
|
||||
userName = [request formValueForKey: @"userName"];
|
||||
username = [request formValueForKey: @"userName"];
|
||||
password = [request formValueForKey: @"password"];
|
||||
language = [request formValueForKey: @"language"];
|
||||
if ([auth checkLogin: userName password: password])
|
||||
if ([auth checkLogin: username password: password])
|
||||
{
|
||||
[self logWithFormat: @"successful login for user '%@'", userName];
|
||||
[self logWithFormat: @"successful login for user '%@'", username];
|
||||
response = [self responseWith204];
|
||||
cookieString = [NSString stringWithFormat: @"%@:%@",
|
||||
userName, password];
|
||||
cookieValue = [NSString stringWithFormat: @"basic %@",
|
||||
[cookieString stringByEncodingBase64]];
|
||||
authCookie = [WOCookie cookieWithName: [auth cookieNameInContext: context]
|
||||
value: cookieValue];
|
||||
[authCookie setPath: @"/"];
|
||||
/* enable this when we have code to determine whether request is HTTPS:
|
||||
[authCookie setIsSecure: YES]; */
|
||||
authCookie = [self _cookieWithUsername: username andPassword: password
|
||||
forAuthenticator: auth];
|
||||
[response addCookie: authCookie];
|
||||
|
||||
supportedLanguages = [[SOGoSystemDefaults sharedSystemDefaults]
|
||||
supportedLanguages];
|
||||
if (language && [supportedLanguages containsObject: language])
|
||||
{
|
||||
ud = [[SOGoUser userWithLogin: userName roles: nil]
|
||||
userDefaults];
|
||||
ud = [[SOGoUser userWithLogin: username] userDefaults];
|
||||
[ud setLanguage: language];
|
||||
[ud synchronize];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"failed login for user '%@'", userName];
|
||||
[self logWithFormat: @"failed login for user '%@'", username];
|
||||
response = [self responseWithStatus: 403];
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) defaultAction
|
||||
- (NSDictionary *) _casRedirectKeys
|
||||
{
|
||||
id <WOActionResults> response;
|
||||
NSDictionary *redirectKeys;
|
||||
NSURL *soURL;
|
||||
|
||||
soURL = [[WOApplication application] soURL];
|
||||
|
||||
redirectKeys = [NSDictionary dictionaryWithObject: [soURL absoluteString]
|
||||
forKey: @"service"];
|
||||
|
||||
return redirectKeys;
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) casProxyAction
|
||||
{
|
||||
SOGoCache *cache;
|
||||
WORequest *request;
|
||||
NSString *pgtId, *pgtIou;
|
||||
|
||||
request = [context request];
|
||||
pgtId = [request formValueForKey: @"pgtId"];
|
||||
pgtIou = [request formValueForKey: @"pgtIou"];
|
||||
if ([pgtId length] && [pgtIou length])
|
||||
{
|
||||
cache = [SOGoCache sharedCache];
|
||||
[cache setCASPGTId: pgtId forPGTIOU: pgtIou];
|
||||
}
|
||||
|
||||
return [self responseWithStatus: 200];
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) _casDefaultAction
|
||||
{
|
||||
WOResponse *response;
|
||||
NSString *login, *newLocation, *oldLocation, *ticket;
|
||||
SOGoCASSession *casSession;
|
||||
SOGoWebAuthenticator *auth;
|
||||
WOCookie *casCookie;
|
||||
|
||||
casCookie = nil;
|
||||
|
||||
login = [[context activeUser] login];
|
||||
if ([login isEqualToString: @"anonymous"])
|
||||
login = nil;
|
||||
if (!login)
|
||||
{
|
||||
ticket = [[context request] formValueForKey: @"ticket"];
|
||||
if ([ticket length])
|
||||
{
|
||||
casSession = [SOGoCASSession CASSessionWithTicket: ticket];
|
||||
login = [casSession login];
|
||||
if ([login length])
|
||||
{
|
||||
auth = [[WOApplication application]
|
||||
authenticatorInContext: context];
|
||||
casCookie = [self _cookieWithUsername: login
|
||||
andPassword: [casSession identifier]
|
||||
forAuthenticator: auth];
|
||||
[casSession updateCache];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (login)
|
||||
{
|
||||
oldLocation = [[self clientObject] baseURLInContext: context];
|
||||
newLocation = [NSString stringWithFormat: @"%@%@",
|
||||
oldLocation, [login stringByEscapingURL]];
|
||||
}
|
||||
else
|
||||
newLocation = [SOGoCASSession CASURLWithAction: @"login"
|
||||
andParameters: [self _casRedirectKeys]];
|
||||
response = [self redirectToLocation: newLocation];
|
||||
if (casCookie)
|
||||
[response addCookie: casCookie];
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) _standardDefaultAction
|
||||
{
|
||||
NSObject <WOActionResults> *response;
|
||||
NSString *login, *oldLocation;
|
||||
|
||||
login = [[context activeUser] login];
|
||||
if (!login || [login isEqualToString: @"anonymous"])
|
||||
response = self;
|
||||
else
|
||||
if ([login isEqualToString: @"anonymous"])
|
||||
login = nil;
|
||||
|
||||
if (login)
|
||||
{
|
||||
oldLocation = [[self clientObject] baseURLInContext: context];
|
||||
response
|
||||
= [self redirectToLocation: [NSString stringWithFormat: @"%@/%@",
|
||||
= [self redirectToLocation: [NSString stringWithFormat: @"%@%@",
|
||||
oldLocation,
|
||||
[login stringByEscapingURL]]];
|
||||
}
|
||||
else
|
||||
response = self;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) defaultAction
|
||||
{
|
||||
SOGoSystemDefaults *sd;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
return ([[sd authenticationType] isEqualToString: @"cas"]
|
||||
? [self _casDefaultAction]
|
||||
: [self _standardDefaultAction]);
|
||||
}
|
||||
|
||||
- (BOOL) isPublicInContext: (WOContext *) localContext
|
||||
{
|
||||
return YES;
|
||||
|
|
|
@ -224,8 +224,6 @@
|
|||
WOResponse *response;
|
||||
|
||||
response = [self responseWithStatus: 200];
|
||||
// [response setHeader: @"text/plain; charset=iso-8859-1"
|
||||
// forKey: @"Content-Type"];
|
||||
[response appendContentString: [self _freeBusyAsText]];
|
||||
|
||||
return response;
|
||||
|
@ -238,7 +236,7 @@
|
|||
SOGoWebAuthenticator *auth;
|
||||
id container;
|
||||
NSCalendarDate *date;
|
||||
NSString *userName, *cookieName;
|
||||
NSString *userName, *cookieName, *appName;
|
||||
|
||||
container = [[self clientObject] container];
|
||||
|
||||
|
@ -261,7 +259,8 @@
|
|||
if ([cookieName length])
|
||||
{
|
||||
cookie = [WOCookie cookieWithName: cookieName value: @"discard"];
|
||||
[cookie setPath: @"/"];
|
||||
appName = [[context request] applicationName];
|
||||
[cookie setPath: [NSString stringWithFormat: @"/%@/", appName]];
|
||||
[cookie setExpires: [date yesterday]];
|
||||
[response addCookie: cookie];
|
||||
}
|
||||
|
|
|
@ -105,6 +105,11 @@
|
|||
protectedBy = "<public>";
|
||||
pageName = "SOGoRootPage";
|
||||
};
|
||||
casProxy = {
|
||||
protectedBy = "<public>";
|
||||
pageName = "SOGoRootPage";
|
||||
actionName = "casProxy";
|
||||
};
|
||||
/* crash = {
|
||||
protectedBy = "<public>";
|
||||
pageName = "SOGoRootPage";
|
||||
|
|
Loading…
Reference in New Issue