See ChangeLog

Monotone-Parent: f5359c59c0bb008203154487db17e1ecdd274c0d
Monotone-Revision: 7c78ba28d583536196a1acf34df5c96f40db238e

Monotone-Author: ludovic@Sophos.ca
Monotone-Date: 2010-12-28T17:42:50
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Ludovic Marcotte 2010-12-28 17:42:50 +00:00
parent 7ffe6ee26a
commit f46ebda105
22 changed files with 738 additions and 53 deletions

View File

@ -21,3 +21,4 @@ SOPE/NGCards/samples/CardGroup.m
SOPE/NGCards/samples/CardVersitRenderer.m
SOPE/NGCards/samples/NGCardsSaxHandler.m
Tests/Integration/config.py
Tests/Integration/teststrings

View File

@ -1,3 +1,12 @@
2010-12-28 Ludovic Marcotte <lmarcotte@inverse.ca>
* Implemented secured sessions. We no longer store in the
browser's cookie the base64 encoded version of username:password
You MUST now set OCSSessionsFolderURL to a value like:
postgresql://sogo:sogo@127.0.0.1:5432/sogo/sogo_sessions_folder
and the database table will be created automatically upon
next SOGo's startup.
2010-12-23 Ludovic Marcotte <lmarcotte@inverse.ca>
* Added the /SOGo/so/<user>/Calendar/reloadWebCalendarsAndRedirect

View File

@ -232,10 +232,18 @@ static BOOL debugLeaks;
}
}
if (ok && [defaults enableEMailAlarms])
if (ok)
{
fm = [GCSFolderManager defaultFolderManager];
[[fm alarmsFolder] createFolderIfNotExists];
// Create the sessions table
[[fm sessionsFolder] createFolderIfNotExists];
// Create the email alarms table, if required
if ([defaults enableEMailAlarms])
{
[[fm alarmsFolder] createFolderIfNotExists];
}
}
return ok;

View File

@ -32,7 +32,7 @@
*/
@class NSString, NSArray, NSURL, NSDictionary, NSException;
@class GCSChannelManager, GCSAlarmsFolder, GCSFolder, GCSFolderType;
@class GCSChannelManager, GCSAlarmsFolder, GCSFolder, GCSFolderType, GCSSessionsFolder;
@interface GCSFolderManager : NSObject
{
@ -73,6 +73,9 @@
/* alarms */
- (GCSAlarmsFolder *)alarmsFolder;
/* sessions */
- (GCSSessionsFolder *)sessionsFolder;
/* folder types */
- (GCSFolderType *)folderTypeWithName:(NSString *)_name;

View File

@ -1,14 +1,15 @@
/*
Copyright (C) 2006-2011 Inverse inc.
Copyright (C) 2004-2007 SKYRIX Software AG
This file is part of OpenGroupware.org.
This file is part of SOGo.
OGo is free software; you can redistribute it and/or modify it under
SOGo 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.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
SOGo 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.
@ -42,6 +43,7 @@
#import "GCSAlarmsFolder.h"
#import "GCSFolder.h"
#import "GCSFolderType.h"
#import "GCSSessionsFolder.h"
#import "GCSSpecialQueries.h"
#import "NSURL+GCS.h"
@ -330,7 +332,12 @@ static NSCharacterSet *asciiAlphaNumericCS = nil;
return [GCSAlarmsFolder alarmsFolderWithFolderManager: self];
}
/* path SQL */
/* sessions */
- (GCSSessionsFolder *)sessionsFolder
{
return [GCSSessionsFolder sessionsFolderWithFolderManager: self];
}
- (NSString *)generateSQLWhereForInternalNames:(NSArray *)_names
exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs

View File

@ -0,0 +1,56 @@
/* GCSSessionsFolder.h - this file is part of SOGo
*
* Copyright (C) 2010-2011 Inverse inc.
*
* Author: Ludovic Marcotte <lmarcotte@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 GCSSESSIONSFOLDER_H
#define GCSSESSIONSFOLDER_H
#import <Foundation/NSObject.h>
@class NSCalendarDate;
@class NSException;
@class NSNumber;
@class NSString;
@class GCSFolderManager;
@interface GCSSessionsFolder : NSObject
{
GCSFolderManager *folderManager;
}
+ (id) sessionsFolderWithFolderManager: (GCSFolderManager *) newFolderManager;
- (void) setFolderManager: (GCSFolderManager *) newFolderManager;
/* operations */
- (void) createFolderIfNotExists;
- (BOOL) canConnectStore;
- (NSDictionary *) recordForEntryWithID: (NSString *) theID;
- (void) deleteRecordForEntryWithID: (NSString *) theID;
- (void) writeRecordForEntryWithID: (NSString *) theID
value: (NSString *) theValue
creationDate: (NSCalendarDate *) theCreationDate
lastSeenDate: (NSCalendarDate *) theLastSeenDate;
@end
#endif /* GCSSESSIONSFOLDER_H */

View File

@ -0,0 +1,357 @@
/* GCSSessionsFolder.m - this file is part of SOGo
*
* Copyright (C) 2010-2011 Inverse inc.
*
* Author: Ludovic Marcotte <lmarcotte@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/NSString.h>
#import <Foundation/NSURL.h>
#import <Foundation/NSUserDefaults.h>
#import <Foundation/NSValue.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSNull+misc.h>
#import <GDLAccess/EOAdaptorChannel.h>
#import <GDLAccess/EOAdaptorContext.h>
#import <GDLAccess/EOAttribute.h>
#import <GDLAccess/EOEntity.h>
#import <GDLAccess/EOSQLQualifier.h>
#import "EOQualifier+GCS.h"
#import "GCSChannelManager.h"
#import "GCSFolderManager.h"
#import "GCSSpecialQueries.h"
#import "GCSStringFormatter.h"
#import "NSURL+GCS.h"
#import "GCSSessionsFolder.h"
static NSString *sessionsFolderURLString = nil;
#warning GCSSessionsFolder should share a common ancestor with GCSFolder
@implementation GCSSessionsFolder
+ (void) initialize
{
NSUserDefaults *ud;
if (!sessionsFolderURLString)
{
ud = [NSUserDefaults standardUserDefaults];
ASSIGN(sessionsFolderURLString, [ud stringForKey: @"OCSSessionsFolderURL"]);
}
}
+ (id) sessionsFolderWithFolderManager: (GCSFolderManager *) newFolderManager
{
GCSAlarmsFolder *newFolder;
if (sessionsFolderURLString)
{
newFolder = [self new];
[newFolder autorelease];
[newFolder setFolderManager: newFolderManager];
}
else
{
[self errorWithFormat: @"'OCSSessionsFolderURL' is not set"];
newFolder = nil;
}
return newFolder;
}
- (void) setFolderManager: (GCSFolderManager *) newFolderManager
{
ASSIGN (folderManager, newFolderManager);
}
/* accessors */
- (NSURL *) _location
{
NSURL *location;
if (sessionsFolderURLString)
location = [NSURL URLWithString: sessionsFolderURLString];
else
{
[self warnWithFormat: @"'OCSSessionsFolderURL' is not set"];
location = nil;
}
return location;
}
- (GCSChannelManager *) _channelManager
{
return [folderManager channelManager];
}
- (NSString *) _storeTableName
{
return [[self _location] gcsTableName];
}
- (EOEntity *) _storeTableEntityForChannel: (EOAdaptorChannel *) tc
{
static EOEntity *entity = nil;
EOAttribute *attribute;
NSString *tableName;
NSString *columns[] = { @"c_id", @"c_value", @"c_creationdate",
@"c_lastseen", nil };
NSString **column;
NSMutableArray *keys;
NSDictionary *types;
if (!entity)
{
entity = [EOEntity new];
tableName = [self _storeTableName];
[entity setName: tableName];
[entity setExternalName: tableName];
types = [[tc specialQueries] sessionsAttributeTypes];
column = columns;
while (*column)
{
attribute = [EOAttribute new];
[attribute setName: *column];
[attribute setColumnName: *column];
[attribute setExternalType: [types objectForKey: *column]];
[entity addAttribute: attribute];
[attribute release];
column++;
}
keys = [NSMutableArray arrayWithCapacity: 1];
[keys addObject: [entity attributeNamed: @"c_id"]];
[entity setPrimaryKeyAttributes: keys];
keys = [NSMutableArray arrayWithCapacity: 3];
[keys addObject: [entity attributeNamed: @"c_value"]];
[keys addObject: [entity attributeNamed: @"c_creationdate"]];
[keys addObject: [entity attributeNamed: @"c_lastseen"]];
[entity setClassProperties: keys];
[entity setAttributesUsedForLocking: [NSArray array]];
}
return entity;
}
/* connection */
- (EOAdaptorChannel *) _acquireStoreChannel
{
return [[self _channelManager] acquireOpenChannelForURL: [self _location]];
}
- (void) _releaseChannel: (EOAdaptorChannel *) _channel
{
[[self _channelManager] releaseChannel:_channel];
}
- (BOOL) canConnectStore
{
return [[self _channelManager] canConnect:[self _location]];
}
- (void) createFolderIfNotExists
{
EOAdaptorChannel *tc;
NSString *sql, *tableName;
GCSSpecialQueries *queries;
tc = [self _acquireStoreChannel];
tableName = [self _storeTableName];
queries = [tc specialQueries];
sql = [NSString stringWithFormat: @"SELECT count(*) FROM %@",
[self _storeTableName]];
if ([tc evaluateExpressionX: sql])
{
sql = [queries createSessionsFolderWithName: tableName];
if (![tc evaluateExpressionX: sql])
[self logWithFormat:
@"sessions folder table '%@' successfully created!",
tableName];
}
else
[tc cancelFetch];
[self _releaseChannel: tc];
}
/* operations */
- (NSDictionary *) recordForEntryWithID: (NSString *) theID
{
EOAdaptorChannel *tc;
EOAdaptorContext *context;
NSString *tableName;
NSException *error;
NSArray *attrs;
NSDictionary *record;
EOEntity *entity;
EOSQLQualifier *qualifier;
tableName = [self _storeTableName];
record = nil;
tc = [self _acquireStoreChannel];
if (tc)
{
context = [tc adaptorContext];
entity = [self _storeTableEntityForChannel: tc];
qualifier = [[EOSQLQualifier alloc] initWithEntity: entity
qualifierFormat:
@"c_id='%@'",
theID];
[qualifier autorelease];
[context beginTransaction];
error = [tc selectAttributesX: [entity attributesUsedForFetch]
describedByQualifier: qualifier
fetchOrder: nil
lock: NO];
if (error)
[self errorWithFormat:@"%s: cannot execute fetch: %@",
__PRETTY_FUNCTION__, error];
else
{
attrs = [tc describeResults: NO];
record = [tc fetchAttributes: attrs withZone: NULL];
[tc cancelFetch];
}
[context rollbackTransaction];
[self _releaseChannel: tc];
}
return record;
}
- (NSDictionary *) _newRecordWithID: (NSString *) theID
value: (NSString *) theValue
creationDate: (NSCalendarDate *) theCreationDate
lastSeenDate: (NSCalendarDate *) theLastSeenDate
{
NSNumber *cd, *ls;
// We check if recId and alarmDate are nil prior calling -timeIntervalSince1970
// Weird gcc optimizations can cause issue here.
cd = [NSNumber numberWithInt: (theCreationDate ? (int)[theCreationDate timeIntervalSince1970] : 0)];
ls = [NSNumber numberWithInt: (theLastSeenDate ? (int)[theLastSeenDate timeIntervalSince1970] : 0)];
return [NSDictionary dictionaryWithObjectsAndKeys: theID, @"c_id",
theValue, @"c_value",
cd, @"c_creationdate",
ls, @"c_lastseen",
nil];
}
- (void) writeRecordForEntryWithID: (NSString *) theID
value: (NSString *) theValue
creationDate: (NSCalendarDate *) theCreationDate
lastSeenDate: (NSCalendarDate *) theLastSeenDate
{
NSDictionary *record, *newRecord;
NSException *error;
EOAdaptorChannel *tc;
EOAdaptorContext *context;
EOEntity *entity;
EOSQLQualifier *qualifier;
tc = [self _acquireStoreChannel];
if (tc)
{
context = [tc adaptorContext];
newRecord = [self _newRecordWithID: theID
value: theValue
creationDate: theCreationDate
lastSeenDate: theLastSeenDate];
record = [self recordForEntryWithID: theID];
entity = [self _storeTableEntityForChannel: tc];
[context beginTransaction];
if (record)
{
qualifier = [[EOSQLQualifier alloc] initWithEntity: entity
qualifierFormat:
@"c_id='%@'",
theID];
[qualifier autorelease];
error = [tc updateRowX: newRecord describedByQualifier: qualifier];
}
else
error = [tc insertRowX: newRecord forEntity: entity];
if (error)
{
[context rollbackTransaction];
[self errorWithFormat:@"%s: cannot write record: %@",
__PRETTY_FUNCTION__, error];
}
else
[context commitTransaction];
[self _releaseChannel: tc];
}
}
- (void) deleteRecordForEntryWithID: (NSString *) theID
{
EOAdaptorChannel *tc;
EOAdaptorContext *context;
EOEntity *entity;
EOSQLQualifier *qualifier;
NSException *error;
tc = [self _acquireStoreChannel];
if (tc)
{
context = [tc adaptorContext];
entity = [self _storeTableEntityForChannel: tc];
qualifier = [[EOSQLQualifier alloc] initWithEntity: entity
qualifierFormat:
@"c_id='%@'",
theID];
[qualifier autorelease];
[context beginTransaction];
error = [tc deleteRowsDescribedByQualifierX: qualifier];
if (error)
{
[context rollbackTransaction];
[self errorWithFormat:@"%s: cannot delete record: %@",
__PRETTY_FUNCTION__, error];
}
else
[context commitTransaction];
[self _releaseChannel: tc];
}
}
@end

View File

@ -37,6 +37,9 @@
- (NSString *) createFolderTableWithName: (NSString *) tableName;
- (NSString *) createFolderACLTableWithName: (NSString *) tableName;
- (NSString *) createSessionsFolderWithName: (NSString *) tableName;
- (NSDictionary *) sessionsAttributeTypes;
@end
@interface EOAdaptorChannel (GCSSpecialQueries)

View File

@ -105,8 +105,26 @@
return nil;
}
- (NSString *) createSessionsFolderWithName: (NSString *) tableName
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSDictionary *) sessionsAttributeTypes
{
[self subclassResponsibility: _cmd];
return nil;
}
@end
//
// PostgreSQL database
//
@implementation GCSPostgreSQLSpecialQueries
- (NSString *) createEMailAlarmsFolderWithName: (NSString *) tableName
@ -168,8 +186,39 @@
return [NSString stringWithFormat: sqlFolderACLFormat, tableName];
}
- (NSString *) createSessionsFolderWithName: (NSString *) tableName
{
static NSString *sqlFolderFormat
= (@"CREATE TABLE %@ ("
@" c_id VARCHAR(255) NOT NULL,"
@" c_value VARCHAR(255) NOT NULL,"
@" c_creationdate INT4 NOT NULL,"
@" c_lastseen INT4 NOT NULL)");
return [NSString stringWithFormat: sqlFolderFormat, tableName];
}
- (NSDictionary *) sessionsAttributeTypes
{
static NSMutableDictionary *types = nil;
if (!types)
{
types = [NSMutableDictionary new];
[types setObject: @"varchar" forKey: @"c_id"];
[types setObject: @"varchar" forKey: @"c_value"];
[types setObject: @"int" forKey: @"c_creationdate"];
[types setObject: @"int" forKey: @"c_lastseen"];
}
return types;
}
@end
//
// MySQL database
//
@implementation GCSMySQLSpecialQueries
- (NSString *) createEMailAlarmsFolderWithName: (NSString *) tableName
@ -231,8 +280,39 @@
return [NSString stringWithFormat: sqlFolderACLFormat, tableName];
}
- (NSString *) createSessionsFolderWithName: (NSString *) tableName
{
static NSString *sqlFolderFormat
= (@"CREATE TABLE %@ ("
@" c_id VARCHAR(255) NOT NULL,"
@" c_value VARCHAR(255) NOT NULL,"
@" c_creationdate INT NOT NULL,"
@" c_lastseen INT NOT NULL)");
return [NSString stringWithFormat: sqlFolderFormat, tableName];
}
- (NSDictionary *) sessionsAttributeTypes
{
static NSMutableDictionary *types = nil;
if (!types)
{
types = [NSMutableDictionary new];
[types setObject: @"varchar" forKey: @"c_id"];
[types setObject: @"varchar" forKey: @"c_value"];
[types setObject: @"int" forKey: @"c_creationdate"];
[types setObject: @"int" forKey: @"c_lastseen"];
}
return types;
}
@end
//
// Oracle database
//
@implementation GCSOracleSpecialQueries
- (NSString *) createEMailAlarmsFolderWithName: (NSString *) tableName
@ -293,4 +373,32 @@
return [NSString stringWithFormat: sqlFolderACLFormat, tableName];
}
- (NSString *) createSessionsFolderWithName: (NSString *) tableName
{
static NSString *sqlFolderFormat
= (@"CREATE TABLE %@ ("
@" c_id VARCHAR2(255) NOT NULL,"
@" c_value VARCHAR2(255) NOT NULL,"
@" c_creationdate INTEGER NOT NULL,"
@" c_lastseen INTEGER NOT NULL)");
return [NSString stringWithFormat: sqlFolderFormat, tableName];
}
- (NSDictionary *) sessionsAttributeTypes
{
static NSMutableDictionary *types = nil;
if (!types)
{
types = [NSMutableDictionary new];
[types setObject: @"varchar2" forKey: @"c_id"];
[types setObject: @"varchar2" forKey: @"c_value"];
[types setObject: @"integer" forKey: @"c_creationdate"];
[types setObject: @"integer" forKey: @"c_lastseen"];
}
return types;
}
@end

View File

@ -32,6 +32,7 @@ libGDLContentStore_HEADER_FILES += \
GCSFolderType.h \
GCSChannelManager.h \
GCSFieldExtractor.h \
GCSSessionsFolder.h \
GCSSpecialQueries.h \
GCSStringFormatter.h \
@ -48,6 +49,7 @@ libGDLContentStore_OBJC_FILES += \
GCSFolderType.m \
GCSChannelManager.m \
GCSFieldExtractor.m \
GCSSessionsFolder.m \
GCSSpecialQueries.m \
GCSStringFormatter.m \

View File

@ -629,14 +629,16 @@ static NSString *sieveScriptName = @"sogo";
return [NSMutableString string];
}
//
// Extract password from basic authentication.
//
- (NSString *) imap4PasswordRenewed: (BOOL) renewed
{
/*
Extract password from basic authentication.
*/
NSURL *imapURL;
NSString *password;
NSURL *imapURL;
// Default account - ie., the account that is provided with a default
// SOGo installation. User-added IMAP accounts will have name >= 1.
if ([nameInContainer isEqualToString: @"0"])
{
imapURL = [self imap4URL];

View File

@ -46,6 +46,7 @@ SOGo_HEADER_FILES = \
NSURL+DAV.h \
\
SOGoAuthenticator.h \
SOGoSession.h \
SOGoCASSession.h \
SOGoDAVAuthenticator.h \
SOGoProxyAuthenticator.h \
@ -108,6 +109,7 @@ SOGo_OBJC_FILES = \
NSString+Utilities.m \
NSURL+DAV.m \
\
SOGoSession.m \
SOGoCASSession.m \
SOGoDAVAuthenticator.m \
SOGoProxyAuthenticator.m \

View File

@ -1,6 +1,6 @@
/* SOGoCache.h - this file is part of SOGo
*
* Copyright (C) 2008-2010 Inverse inc.
* Copyright (C) 2008-2011 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Ludovic Marcotte <lmarcotte@inverse.ca>
@ -92,7 +92,9 @@
forLogin: (NSString *) login;
- (NSString *) userSettingsForLogin: (NSString *) theLogin;
/* CAS support */
//
// CAS support
//
- (NSString *) CASTicketFromIdentifier: (NSString *) identifier;
- (NSString *) CASSessionWithTicket: (NSString *) ticket;
- (void) setCASSession: (NSString *) casSession
@ -103,9 +105,11 @@
- (void) setCASPGTId: (NSString *) pgtId
forPGTIOU: (NSString *) pgtIou;
//
// ACL caching support
//
- (void) setACLs: (NSDictionary *) theACLs
forPath: (NSString *) thePath;
- (NSMutableDictionary *) aclsForPath: (NSString *) thePath;
@end

View File

@ -36,8 +36,13 @@
* <uid>+attributes value = NSMutableDictionary instance > user's LDAP attributes
* <object path>+acl value = NSDictionary instance > ACLs on an object at specified path
* <groupname>+<domain> value = NSString instance (array components separated by ",") or group member logins for a specific group in domain
* cas-id:< > value =
* cas-ticket:< > value =
* cas-pgtiou:< > value =
* session:< > value =
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSData.h>
#import <Foundation/NSDictionary.h>
@ -437,7 +442,9 @@ static memcached_st *handle = NULL;
return [self _valuesOfType: @"settings" forKey: theLogin];
}
/* CAS session support */
//
// CAS session support
//
- (NSString *) CASTicketFromIdentifier: (NSString *) identifier
{
return [self valueForKey: [NSString stringWithFormat: @"cas-id:%@",
@ -481,7 +488,7 @@ static memcached_st *handle = NULL;
}
//
//
// ACL caching code
//
- (void) setACLs: (NSDictionary *) theACLs
forPath: (NSString *) thePath

View File

@ -1,14 +1,15 @@
/*
Copyright (C) 2007-2011 Inverse inc.
Copyright (C) 2004 SKYRIX Software AG
This file is part of OpenGroupware.org.
This file is part of SOGo.
OGo is free software; you can redistribute it and/or modify it under
SOGo 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.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
SOGo 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.

View File

@ -1,6 +1,6 @@
/* SOGoProxyAuthenticator.h - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
* Copyright (C) 2009-2011 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*

View File

@ -402,7 +402,6 @@
grace: grace];
}
return checkOK;
}

View File

@ -1,6 +1,6 @@
/* SOGoWebAuthenticator.m - this file is part of SOGo
*
* Copyright (C) 2007-2010 Inverse inc.
* Copyright (C) 2007-2011 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
@ -32,13 +32,16 @@
#import <NGObjWeb/WOResponse.h>
#import <NGExtensions/NSCalendarDate+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSNull+misc.h>
#import <NGLdap/NGLdapConnection.h>
#import <MainUI/SOGoRootPage.h>
#import "SOGoCache.h"
#import "SOGoCASSession.h"
#import "SOGoConstants.h"
#import "SOGoPermissions.h"
#import "SOGoSession.h"
#import "SOGoSystemDefaults.h"
#import "SOGoUser.h"
#import "SOGoUserManager.h"
@ -59,12 +62,32 @@
- (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd
{
{
NSString *username, *password, *value;
SOGoPasswordPolicyError perr;
int expire, grace;
return [self checkLogin: _login
password: _pwd
// We check for the existence of the session in the database/memcache
// and we extract the real password from it. Here,
//
// _login == userKey
// _pwd == sessionKey
//
// If the session isn't present in the database, we fail the login process.
//
value = [SOGoSession valueForSessionKey: _pwd];
if (!value)
return NO;
[SOGoSession decodeValue: value
usingKey: _login
login: &username
password: &password];
return [self checkLogin: username
password: password
perr: &perr
expire: &expire
grace: &grace];
@ -97,13 +120,16 @@
expire: _expire
grace: _grace];
// [self logWithFormat: @"Checked login with ppolicy enabled: %d %d %d", *_perr, *_expire, *_grace];
//[self logWithFormat: @"Checked login with ppolicy enabled: %d %d %d", *_perr, *_expire, *_grace];
// It's important to return the real value here. The callee will handle
// the return code and check for the _perr value.
return rc;
}
//
//
//
- (SOGoUser *) userInContext: (WOContext *)_ctx
{
static SOGoUser *anonymous = nil;
@ -124,20 +150,64 @@
- (NSString *) passwordInContext: (WOContext *) context
{
NSArray *creds;
NSString *auth, *password;
NSArray *creds;
auth = [[context request]
cookieValueForKey: [self cookieNameInContext: context]];
creds = [self parseCredentials: auth];
if ([creds count] > 1)
password = [creds objectAtIndex: 1];
{
NSString *login;
[SOGoSession decodeValue: [SOGoSession valueForSessionKey: [creds objectAtIndex: 1]]
usingKey: [creds objectAtIndex: 0]
login: &login
password: &password];
}
else
password = nil;
return password;
}
//
// We overwrite SOPE's method in order to proper retrieve
// the username from the cookie.
//
- (NSString *) checkCredentials: (NSString *)_creds
{
NSString *login, *pwd, *userKey, *sessionKey;
NSArray *creds;
SOGoPasswordPolicyError perr;
int expire, grace;
if (![(creds = [self parseCredentials:_creds]) isNotEmpty])
return nil;
userKey = [creds objectAtIndex:0];
if ([userKey isEqualToString:@"anonymous"])
return @"anonymous";
sessionKey = [creds objectAtIndex:1];
[SOGoSession decodeValue: [SOGoSession valueForSessionKey: sessionKey]
usingKey: userKey
login: &login
password: &pwd];
if (![self checkLogin: login
password: pwd
perr: &perr
expire: &expire
grace: &grace])
return nil;
return login;
}
- (NSString *) imapPasswordInContext: (WOContext *) context
forServer: (NSString *) imapServer
forceRenew: (BOOL) renew
@ -176,12 +246,12 @@
return [SOGoUser userWithLogin: login roles: roles];
}
//
// This is called by SoObjectRequestHandler prior doing any significant
// processing to allow the authenticator to reject invalid requests.
//
- (WOResponse *) preprocessCredentialsInContext: (WOContext *) context
{
/*
This is called by SoObjectRequestHandler prior doing any significant
processing to allow the authenticator to reject invalid requests.
*/
WOResponse *response;
NSString *auth;

View File

@ -51,7 +51,7 @@
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserManager.h>
#import <SOGo/SOGoWebAuthenticator.h>
#import <SOGo/SOGoSession.h>
#import <SOGo/SOGoConstants.h>
#import "SOGoRootPage.h"
@ -70,18 +70,37 @@
forAuthenticator: (SOGoWebAuthenticator *) auth
{
WOCookie *authCookie;
NSString *cookieValue, *cookieString, *appName;
NSString *cookieValue, *cookieString, *appName, *sessionKey, *userKey, *securedPassword;
//
// We create a new cookie - thus we create a new session
// associated to the user. For security, we generate:
//
// A- a session key
// B- a user key
//
// In memcached, the session key will be associated to the user's password
// which will be XOR'ed with the user key.
//
sessionKey = [SOGoSession generateKeyForLength: 16];
userKey = [SOGoSession generateKeyForLength: 64];
NSString *value = [NSString stringWithFormat: @"%@:%@", username, password];
securedPassword = [SOGoSession securedValue: value usingKey: userKey];
[SOGoSession setValue: securedPassword forSessionKey: sessionKey];
//cookieString = [NSString stringWithFormat: @"%@:%@",
// username, password];
cookieString = [NSString stringWithFormat: @"%@:%@",
username, password];
userKey, sessionKey];
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;
}
@ -107,7 +126,9 @@
return locationCookie;
}
/* actions */
//
//
//
- (WOResponse *) _responseWithLDAPPolicyError: (int) error
{
NSDictionary *jsonError;
@ -164,8 +185,9 @@
response = [self responseWithStatus: 200
andJSONRepresentation: json];
authCookie = [self _cookieWithUsername: username andPassword: password
forAuthenticator: auth];
authCookie = [self _cookieWithUsername: username
andPassword: password
forAuthenticator: auth];
[response addCookie: authCookie];
supportedLanguages = [[SOGoSystemDefaults sharedSystemDefaults]
@ -369,11 +391,6 @@
return [[SOGoSystemDefaults sharedSystemDefaults] supportedLanguages];
}
// - (NSString *) language
// {
// return [SOGoUser language];
// }
- (NSString *) languageText
{
NSString *text;
@ -397,7 +414,7 @@
- (WOResponse *) changePasswordAction
{
NSString *username, *password, *newPassword;
NSString *username, *password, *newPassword, *value;
SOGoUserManager *um;
SOGoPasswordPolicyError error;
WOResponse *response;
@ -405,11 +422,22 @@
NSDictionary *message;
SOGoWebAuthenticator *auth;
WOCookie *authCookie;
NSArray *creds;
request = [context request];
message = [[request contentAsString] objectFromJSONString];
username = [message objectForKey: @"userName"];
password = [message objectForKey: @"password"];
auth = [[WOApplication application]
authenticatorInContext: context];
value = [[context request]
cookieValueForKey: [auth cookieNameInContext: context]];
creds = [auth parseCredentials: value];
[SOGoSession decodeValue: [SOGoSession valueForSessionKey: [creds objectAtIndex: 1]]
usingKey: [creds objectAtIndex: 0]
login: &username
password: &password];
newPassword = [message objectForKey: @"newPassword"];
um = [SOGoUserManager sharedUserManager];
@ -420,9 +448,10 @@
newPassword: newPassword
perr: &error])
{
// We delete the previous session
[SOGoSession deleteValueForSessionKey: [creds objectAtIndex: 1]];
response = [self responseWith204];
auth = [[WOApplication application]
authenticatorInContext: context];
authCookie = [self _cookieWithUsername: username
andPassword: newPassword
forAuthenticator: auth];

View File

@ -41,6 +41,7 @@
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/SOGoUserFolder.h>
#import <SOGo/SOGoSession.h>
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/NSCalendarDate+SOGo.h>
#import <SOGo/NSDictionary+Utilities.h>
@ -275,11 +276,13 @@
- (id <WOActionResults>) logoffAction
{
SOGoWebAuthenticator *auth;
NSString *userName, *value;
WOResponse *response;
WOCookie *cookie;
NSCalendarDate *date;
NSString *userName;
WOCookie *cookie;
NSArray *creds;
userName = [[context activeUser] login];
[self logWithFormat: @"user '%@' logged off", userName];
@ -288,6 +291,18 @@
date = [NSCalendarDate calendarDate];
[date setTimeZone: [NSTimeZone timeZoneWithAbbreviation: @"GMT"]];
// We cleanup the memecached/database session cache. We do this before
// invoking _logoutCookieWithDate: in order to obtain its value.
auth = [[self clientObject] authenticatorInContext: context];
if ([auth respondsToSelector: @selector (cookieNameInContext:)])
{
value = [[context request] cookieValueForKey: [auth cookieNameInContext: context]];
creds = [auth parseCredentials: value];
if ([creds count] > 1)
[SOGoSession deleteValueForSessionKey: [creds objectAtIndex: 1]];
}
cookie = [self _logoutCookieWithDate: date];
if (cookie)
[response addCookie: cookie];
@ -298,6 +313,7 @@
forKey: @"Cache-Control"];
[response setHeader: @"no-cache" forKey: @"Pragma"];
return response;
}

View File

@ -98,6 +98,7 @@ function onLoginCallback(http) {
if (http.status == 200) {
// Make sure browser's cookies are enabled
var loginCookie = readLoginCookie();
if (!loginCookie) {
SetLogMessage("errorMessage", _("cookiesNotEnabled"));
submitBtn.disabled = false;