From f44212f0e6d8639852e225b38e9741c3176be196 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Fri, 24 Jun 2011 19:19:50 +0000 Subject: [PATCH] See ChangeLog Monotone-Parent: c0fdc871e5cf2faadbed16ce1c4275647d0928b4 Monotone-Revision: 178cf4e5311729ac333305676b5ad2aa35a7ca71 Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2011-06-24T19:19:50 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 47 +++++++ SoObjects/Contacts/SOGoContactFolders.m | 32 +++-- SoObjects/SOGo/SOGoSession.h | 2 + SoObjects/SOGo/SOGoSession.m | 34 ++++- SoObjects/SOGo/SOGoSystemDefaults.h | 8 +- SoObjects/SOGo/SOGoSystemDefaults.m | 78 +++++++++++- SoObjects/SOGo/SOGoUser.h | 4 +- SoObjects/SOGo/SOGoUser.m | 52 +++++++- SoObjects/SOGo/SOGoUserManager.h | 10 +- SoObjects/SOGo/SOGoUserManager.m | 132 +++++++++++++++----- SoObjects/SOGo/SOGoWebAuthenticator.h | 4 +- SoObjects/SOGo/SOGoWebAuthenticator.m | 59 +++++---- SoObjects/SOGo/SQLSource.m | 3 +- UI/MainUI/English.lproj/Localizable.strings | 1 + UI/MainUI/French.lproj/Localizable.strings | 1 + UI/MainUI/SOGoRootPage.m | 46 ++++--- UI/MainUI/SOGoUserHomePage.m | 36 ++++-- UI/Templates/MainUI/SOGoRootPage.wox | 10 ++ UI/WebServerResources/SOGoRootPage.js | 10 +- 19 files changed, 462 insertions(+), 107 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7a1b2b3fd..ae9e16e3e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,50 @@ +2011-06-24 Francis Lachapelle + + * SoObjects/SOGo/SOGoSystemDefaults.m (-loginDomains): new method + that returns the content of the defaults key SOGoLoginDomains, which + is a list of domains that should be selectable from the Web login page. + (-visibleDomainsForDomain:): new method that returns a list of + domains visible from the specified domain and defined by the + defaults key SOGoDomainsVisibility, which is a list of lists of domains. + + * SoObjects/SOGo/SQLSource.m + (-_lookupContactEntry:considerEmail:): when an IMAP login field is + defined, only consider the value from the table if it's not null. + + * SoObjects/SOGo/SOGoSession.m + (+decodeValue:usingKey:login:domain:password:): now accepts the + form login@domain. The domain must be part of the SOGoLoginDomains. + + * SoObjects/SOGo/SOGoUserManager.m + (-getImapLoginForUID:inDomain:): the method now accepts the + user's domain. + (_sourceCheckLogin:andPassword:domain:perr:expire:grace:): idem. + (checkLogin:password:domain:perr:expire:grace:): idem. + (_fillContactInfosForUser:withUIDorEmail:inDomain:): idem. + (-contactInfosForUserWithUIDorEmail:inDomain:): idem. + (-contactInfosForUserWithUIDorEmail:): now accepts the form login@domain. + + * SoObjects/SOGo/SOGoWebAuthenticator.m (-checkCredentials:): + perform the authentication in a domain when specified in the + web session. + + * SoObjects/Contacts/SOGoContactFolders.m (-appendSystemSources): + when using domains, append addressbooks from visible domains. + + * SoObjects/SOGo/SOGoUser.m (-initWithLogin:roles:trust:): now + accepts the form login@domain. + (-loginInDomain): new method. When bound to a domain, it returns + the username part of the login. + + * UI/MainUI/SOGoUserHomePage.m (-_usersForResults:inDomain:): was + _usersResponseForResults:. Now returns only an array + containing the matching users. + (-usersSearchAction): added the results from the visible domains, + if the defaults key SOGoDomainsVisibility is defined. + + * UI/MainUI/SOGoRootPage.m (-connectAction): a domain from the + defaults key SOGoLoginDomains can now be received. + 2011-06-23 Wolfgang Sourdeau * OpenChange/MAPIStoreTypes.h: removed PRIx64 definitions, that diff --git a/SoObjects/Contacts/SOGoContactFolders.m b/SoObjects/Contacts/SOGoContactFolders.m index b208d0805..f0f3574f3 100644 --- a/SoObjects/Contacts/SOGoContactFolders.m +++ b/SoObjects/Contacts/SOGoContactFolders.m @@ -40,6 +40,7 @@ #import #import #import +#import #import #import "SOGoContactGCSFolder.h" @@ -62,7 +63,8 @@ - (NSException *) appendSystemSources { SOGoUserManager *um; - NSEnumerator *sourceIDs; + SOGoSystemDefaults *sd; + NSEnumerator *sourceIDs, *domains; NSString *currentSourceID, *srcDisplayName, *domain; SOGoContactSourceFolder *currentFolder; SOGoUser *currentUser; @@ -75,18 +77,24 @@ { domain = [currentUser domain]; um = [SOGoUserManager sharedUserManager]; - sourceIDs = [[um addressBookSourceIDsInDomain: domain] - objectEnumerator]; - while ((currentSourceID = [sourceIDs nextObject])) + sd = [SOGoSystemDefaults sharedSystemDefaults]; + domains = [[sd visibleDomainsForDomain: domain] objectEnumerator]; + while (domain) { - srcDisplayName - = [um displayNameForSourceWithID: currentSourceID]; - currentFolder = [SOGoContactSourceFolder - folderWithName: currentSourceID - andDisplayName: srcDisplayName - inContainer: self]; - [currentFolder setSource: [um sourceWithID: currentSourceID]]; - [subFolders setObject: currentFolder forKey: currentSourceID]; + sourceIDs = [[um addressBookSourceIDsInDomain: domain] + objectEnumerator]; + while ((currentSourceID = [sourceIDs nextObject])) + { + srcDisplayName + = [um displayNameForSourceWithID: currentSourceID]; + currentFolder = [SOGoContactSourceFolder + folderWithName: currentSourceID + andDisplayName: srcDisplayName + inContainer: self]; + [currentFolder setSource: [um sourceWithID: currentSourceID]]; + [subFolders setObject: currentFolder forKey: currentSourceID]; + } + domain = [domains nextObject]; } } } diff --git a/SoObjects/SOGo/SOGoSession.h b/SoObjects/SOGo/SOGoSession.h index 3094f619b..f0b5fc128 100644 --- a/SoObjects/SOGo/SOGoSession.h +++ b/SoObjects/SOGo/SOGoSession.h @@ -3,6 +3,7 @@ * Copyright (C) 2010-2011 Inverse inc. * * Author: Ludovic Marcotte + * Francis Lachapelle * * 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 @@ -40,6 +41,7 @@ + (void) decodeValue: (NSString *) theValue usingKey: (NSString *) theKey login: (NSString **) theLogin + domain: (NSString **) theDomain password: (NSString **) thePassword; @end diff --git a/SoObjects/SOGo/SOGoSession.m b/SoObjects/SOGo/SOGoSession.m index 57c0ee98f..97a2c8117 100644 --- a/SoObjects/SOGo/SOGoSession.m +++ b/SoObjects/SOGo/SOGoSession.m @@ -3,6 +3,8 @@ * Copyright (C) 2010-2011 Inverse inc. * * Author: Ludovic Marcotte + * Francis Lachapelle + * * 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 @@ -24,6 +26,7 @@ #include "SOGoCache.h" +#import #import #import @@ -38,6 +41,8 @@ #include #include +#import "SOGoSystemDefaults.h" + @implementation SOGoSession + (NSString *) valueForSessionKey: (NSString *) theSessionKey @@ -221,16 +226,24 @@ return s; } -// -// -// +/** + * + * @param theValue + * @param theKey + * @param theLogin + * @param theDomain + * @param thePassword + * @see [SOGoUser initWithLogin:roles:trust:] + */ + (void) decodeValue: (NSString *) theValue usingKey: (NSString *) theKey login: (NSString **) theLogin + domain: (NSString **) theDomain password: (NSString **) thePassword { NSString *decodedValue; NSRange r; + SOGoSystemDefaults *sd; decodedValue = [SOGoSession valueFromSecuredValue: theValue usingKey: theKey]; @@ -238,6 +251,21 @@ r = [decodedValue rangeOfString: @":"]; *theLogin = [decodedValue substringToIndex: r.location]; *thePassword = [decodedValue substringFromIndex: r.location+1]; + + r = [*theLogin rangeOfString: @"@" options: NSBackwardsSearch]; + if (r.location != NSNotFound) + { + // The domain is probably appended to the username; + // make sure it is defined as a login domain in the configuration. + sd = [SOGoSystemDefaults sharedSystemDefaults]; + *theDomain = [*theLogin substringFromIndex: (r.location + r.length)]; + if ([[sd loginDomains] containsObject: *theDomain]) + *theLogin = [*theLogin substringToIndex: r.location]; + else + *theDomain = nil; + } + else + *theDomain = nil; } @end diff --git a/SoObjects/SOGo/SOGoSystemDefaults.h b/SoObjects/SOGo/SOGoSystemDefaults.h index 138d9f86a..09e5cce65 100644 --- a/SoObjects/SOGo/SOGoSystemDefaults.h +++ b/SoObjects/SOGo/SOGoSystemDefaults.h @@ -1,8 +1,9 @@ /* SOGoSystemDefaults.h - this file is part of SOGo * - * Copyright (C) 2009-2010 Inverse inc. + * Copyright (C) 2009-2011 Inverse inc. * * Author: Wolfgang Sourdeau + * Francis Lachapelle * * 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 @@ -26,10 +27,15 @@ #import @interface SOGoSystemDefaults : SOGoDomainDefaults +{ + NSArray *loginDomains; +} + (SOGoSystemDefaults *) sharedSystemDefaults; - (NSArray *) domainIds; +- (NSArray *) loginDomains; +- (NSArray *) visibleDomainsForDomain: (NSString *) domain; - (BOOL) crashOnSessionCreate; - (BOOL) debugRequests; diff --git a/SoObjects/SOGo/SOGoSystemDefaults.m b/SoObjects/SOGo/SOGoSystemDefaults.m index 7b4d680fc..72aaa3556 100644 --- a/SoObjects/SOGo/SOGoSystemDefaults.m +++ b/SoObjects/SOGo/SOGoSystemDefaults.m @@ -1,8 +1,9 @@ /* SOGoSystemDefaults.m - this file is part of SOGo * - * Copyright (C) 2009-2010 Inverse inc. + * Copyright (C) 2009-2011 Inverse inc. * * Author: Wolfgang Sourdeau + * Francis Lachapelle * * 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 @@ -23,13 +24,15 @@ #import #import +#import #import #import -#import +#import #import #import +#import "NSArray+Utilities.h" #import "NSDictionary+Utilities.h" #import "SOGoStartupLogger.h" @@ -152,6 +155,22 @@ BootstrapNSUserDefaults () return sharedSystemDefaults; } +- (id) init +{ + if ((self = [super init])) + { + loginDomains = nil; + } + + return self; +} + +- (void) dealloc +{ + [loginDomains release]; + [super dealloc]; +} + - (BOOL) migrate { static NSDictionary *migratedKeys = nil; @@ -174,6 +193,61 @@ BootstrapNSUserDefaults () return [[self dictionaryForKey: @"domains"] allKeys]; } +- (NSArray *) loginDomains +{ + NSMutableArray *filteredLoginDomains; + NSArray *domains; + NSEnumerator *objects; + id currentObject; + + if (self->loginDomains == nil) + { + filteredLoginDomains = [NSMutableArray arrayWithArray: [self stringArrayForKey: @"SOGoLoginDomains"]]; + domains = [self domainIds]; + objects = [filteredLoginDomains objectEnumerator]; + while ((currentObject = [objects nextObject])) + { + if (![domains containsObject: currentObject]) + { + [filteredLoginDomains removeObject: currentObject]; + [self warnWithFormat: @"SOGoLoginDomains contains an invalid domain : %@", currentObject]; + } + } + + ASSIGN (self->loginDomains, filteredLoginDomains); + } + + return self->loginDomains; +} + +- (NSArray *) visibleDomainsForDomain: (NSString *) domain +{ + NSMutableArray *domains; + NSArray *definedDomains, *visibleDomains, *currentGroup; + NSEnumerator *groups; + NSString *currentDomain; + + definedDomains = [self domainIds]; + visibleDomains = [self arrayForKey: @"SOGoDomainsVisibility"]; + domains = [NSMutableArray array]; + groups = [visibleDomains objectEnumerator]; + while ((currentGroup = (NSArray *)[groups nextObject])) + { + if ([currentGroup containsObject: domain]) + [domains addObjectsFromArray: currentGroup]; + } + + // Remove lookup domain from list + groups = [domains objectEnumerator]; + while ((currentDomain = [groups nextObject])) + { + if ([currentDomain isEqualToString: domain]) + [domains removeObject: currentDomain]; + } + + return [domains uniqueObjects]; +} + /* System-level only */ - (BOOL) crashOnSessionCreate diff --git a/SoObjects/SOGo/SOGoUser.h b/SoObjects/SOGo/SOGoUser.h index 2a003e3cc..7400021a9 100644 --- a/SoObjects/SOGo/SOGoUser.h +++ b/SoObjects/SOGo/SOGoUser.h @@ -59,7 +59,7 @@ SOGoUserSettings *_settings; SOGoUserFolder *homeFolder; NSString *currentPassword; - NSString *domainId; + NSString *loginInDomain; NSString *language; NSArray *allEmails; NSMutableArray *mailAccounts; @@ -84,6 +84,8 @@ - (void) setCurrentPassword: (NSString *) newPassword; - (NSString *) currentPassword; +- (NSString *) loginInDomain; + /* properties */ - (NSString *) domain; diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index 1fcc859bb..a58afc1e3 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -122,21 +122,49 @@ return user; } +/** + * Return a new instance for the login name, which can be appended by a + * domain name. + * + * @param newLogin a login name optionally follow by @domain + * @param newRoles + * @param b is set to YES if newLogin can be trust + * @see loginInDomain + * @see [SOGoSession decodeValue:usingKey:login:domain:password:] + */ - (id) initWithLogin: (NSString *) newLogin roles: (NSArray *) newRoles trust: (BOOL) b { SOGoUserManager *um; - NSString *realUID; + SOGoSystemDefaults *sd; + NSString *realUID, *uid, *domain; + NSRange r; _defaults = nil; _settings = nil; + uid = nil; + domain = nil; + if ([newLogin isEqualToString: @"anonymous"] || [newLogin isEqualToString: @"freebusy"]) realUID = newLogin; else { + r = [newLogin rangeOfString: @"@" options: NSBackwardsSearch]; + if (r.location != NSNotFound) + { + // The domain is probably appended to the username; + // make sure it is a defined domain in the configuration. + sd = [SOGoSystemDefaults sharedSystemDefaults]; + domain = [newLogin substringFromIndex: (r.location + r.length)]; + if ([[sd domainIds] containsObject: domain]) + newLogin = [newLogin substringToIndex: r.location]; + else + domain = nil; + } + newLogin = [newLogin stringByReplacingString: @"%40" withString: @"@"]; if (b) @@ -144,9 +172,19 @@ else { um = [SOGoUserManager sharedUserManager]; - realUID = [[um contactInfosForUserWithUIDorEmail: newLogin] + realUID = [[um contactInfosForUserWithUIDorEmail: newLogin + inDomain: domain] objectForKey: @"c_uid"]; } + + if (domain) + { + // When the user is associated to a domain, the [SOGoUser login] + // method returns the combination login@domain while + // [SOGoUser loginInDomain] only returns the login. + uid = [NSString stringWithString: realUID]; + realUID = [NSString stringWithFormat: @"%@@%@", realUID, domain]; + } } if ([realUID length]) @@ -156,6 +194,7 @@ allEmails = nil; currentPassword = nil; cn = nil; + ASSIGN (loginInDomain, (uid ? uid : realUID)); _defaults = nil; _domainDefaults = nil; _settings = nil; @@ -180,6 +219,7 @@ [mailAccounts release]; [currentPassword release]; [cn release]; + [loginInDomain release]; [language release]; [super dealloc]; } @@ -199,6 +239,11 @@ return currentPassword; } +- (NSString *) loginInDomain +{ + return loginInDomain; +} + - (id) _fetchFieldForUser: (NSString *) field { NSDictionary *contactInfos; @@ -518,7 +563,8 @@ // 1. login imapLogin = [[SOGoUserManager sharedUserManager] - getImapLoginForUID: login]; + getImapLoginForUID: [self loginInDomain] + inDomain: [self domain]]; [mailAccount setObject: imapLogin forKey: @"userName"]; // 2. server diff --git a/SoObjects/SOGo/SOGoUserManager.h b/SoObjects/SOGo/SOGoUserManager.h index 558a3bbd9..c24d25515 100644 --- a/SoObjects/SOGo/SOGoUserManager.h +++ b/SoObjects/SOGo/SOGoUserManager.h @@ -1,8 +1,9 @@ /* SOGoUserManager.h - this file is part of SOGo * - * Copyright (C) 2007-2010 Inverse inc. + * Copyright (C) 2007-2011 Inverse inc. * * Author: Wolfgang Sourdeau + * Francis Lachapelle * * 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 @@ -65,6 +66,8 @@ - (NSDictionary *) metadataForSourceID: (NSString *) sourceID; - (NSString *) displayNameForSourceWithID: (NSString *) sourceID; - (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid; +- (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid + inDomain: (NSString *) domain; - (NSArray *) fetchContactsMatching: (NSString *) match inDomain: (NSString *) domain; - (NSArray *) fetchUsersMatching: (NSString *) filter @@ -73,17 +76,20 @@ - (NSString *) getCNForUID: (NSString *) uid; - (NSString *) getEmailForUID: (NSString *) uid; - (NSString *) getFullEmailForUID: (NSString *) uid; -- (NSString *) getImapLoginForUID: (NSString *) uid; +- (NSString *) getImapLoginForUID: (NSString *) uid + inDomain: (NSString *) domain; - (NSString *) getUIDForEmail: (NSString *) email; - (NSString *) getLoginForDN: (NSString *) theDN; - (BOOL) checkLogin: (NSString *) _login password: (NSString *) _pwd + domain: (NSString *) _domain perr: (SOGoPasswordPolicyError *) _perr expire: (int *) _expire grace: (int *) _grace; - (BOOL) changePasswordForLogin: (NSString *) login + inDomain: (NSString *) domain oldPassword: (NSString *) oldPassword newPassword: (NSString *) newPassword perr: (SOGoPasswordPolicyError *) perr; diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index 36c55da2a..6788927dd 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -3,6 +3,7 @@ * Copyright (C) 2007-2011 Inverse inc. * * Author: Wolfgang Sourdeau + * Francis Lachapelle * * 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 @@ -27,6 +28,7 @@ #import #import #import +#import #import #import "NSArray+Utilities.h" @@ -335,14 +337,15 @@ } - (NSString *) getImapLoginForUID: (NSString *) uid + inDomain: (NSString *) domain { NSDictionary *contactInfos; - NSString *domain, *login; + NSString *login; SOGoDomainDefaults *dd; - contactInfos = [self contactInfosForUserWithUIDorEmail: uid]; + contactInfos = [self contactInfosForUserWithUIDorEmail: uid + inDomain: domain]; login = [contactInfos objectForKey: @"c_imaplogin"]; - domain = [contactInfos objectForKey: @"c_domain"]; if (login == nil) { if ([domain length]) @@ -366,6 +369,7 @@ } - (BOOL) _sourceChangePasswordForLogin: (NSString *) login + inDomain: (NSString *) domain oldPassword: (NSString *) oldPassword newPassword: (NSString *) newPassword perr: (SOGoPasswordPolicyError *) perr @@ -377,14 +381,14 @@ didChange = NO; - authIDs = [[self authenticationSourceIDsInDomain: nil] objectEnumerator]; + authIDs = [[self authenticationSourceIDsInDomain: domain] objectEnumerator]; while (!didChange && (currentID = [authIDs nextObject])) { sogoSource = [_sources objectForKey: currentID]; didChange = [sogoSource changePasswordForLogin: login - oldPassword: oldPassword - newPassword: newPassword - perr: perr]; + oldPassword: oldPassword + newPassword: newPassword + perr: perr]; } return didChange; @@ -392,9 +396,10 @@ - (BOOL) _sourceCheckLogin: (NSString *) login andPassword: (NSString *) password - perr: (SOGoPasswordPolicyError *) perr - expire: (int *) expire - grace: (int *) grace + domain: (NSString *) domain + perr: (SOGoPasswordPolicyError *) perr + expire: (int *) expire + grace: (int *) grace { NSObject *sogoSource; NSEnumerator *authIDs; @@ -403,7 +408,7 @@ checkOK = NO; - authIDs = [[self authenticationSourceIDsInDomain: nil] objectEnumerator]; + authIDs = [[self authenticationSourceIDsInDomain: domain] objectEnumerator]; while (!checkOK && (currentID = [authIDs nextObject])) { sogoSource = [_sources objectForKey: currentID]; @@ -419,18 +424,23 @@ - (BOOL) checkLogin: (NSString *) _login password: (NSString *) _pwd + domain: (NSString *) _domain perr: (SOGoPasswordPolicyError *) _perr expire: (int *) _expire grace: (int *) _grace { - NSString *dictPassword, *jsonUser; + NSString *dictPassword, *username, *jsonUser; NSMutableDictionary *currentUser; BOOL checkOK; // We check for cached passwords. If the entry is cached, we // check this immediately. If not, we'll go directly at the // authentication source and try to validate there, then cache it. - jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: _login]; + if (_domain) + username = [NSString stringWithFormat: @"%@@%@", _login, _domain]; + else + username = _login; + jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: username]; currentUser = [jsonUser objectFromJSONString]; dictPassword = [currentUser objectForKey: @"password"]; if (currentUser && dictPassword) @@ -439,10 +449,11 @@ //NSLog(@"Password cache hit for user %@", _login); } else if ([self _sourceCheckLogin: _login - andPassword: _pwd - perr: _perr - expire: _expire - grace: _grace]) + andPassword: _pwd + domain: _domain + perr: _perr + expire: _expire + grace: _grace]) { checkOK = YES; if (!currentUser) @@ -458,7 +469,7 @@ [currentUser setObject: [_pwd asSHA1String] forKey: @"password"]; [[SOGoCache sharedCache] setUserAttributes: [currentUser jsonRepresentation] - forLogin: _login]; + forLogin: username]; } else checkOK = NO; @@ -473,19 +484,20 @@ sources = [[_sources allValues] objectEnumerator]; while ((currentSource = [sources nextObject])) - if ([currentSource conformsToProtocol: @protocol(SOGoDNSource)] && - [currentSource bindAsCurrentUser] && - [currentSource lookupDNByLogin: _login]) - { - [currentSource setBindDN: [currentSource lookupDNByLogin: _login]]; - [currentSource setBindPassword: _pwd]; - } + if ([currentSource conformsToProtocol: @protocol(SOGoDNSource)] && + [currentSource bindAsCurrentUser] && + [currentSource lookupDNByLogin: _login]) + { + [currentSource setBindDN: [currentSource lookupDNByLogin: _login]]; + [currentSource setBindPassword: _pwd]; + } } return checkOK; } - (BOOL) changePasswordForLogin: (NSString *) login + inDomain: (NSString *) domain oldPassword: (NSString *) oldPassword newPassword: (NSString *) newPassword perr: (SOGoPasswordPolicyError *) perr @@ -499,9 +511,10 @@ dictPassword = [currentUser objectForKey: @"password"]; if ([self _sourceChangePasswordForLogin: login - oldPassword: oldPassword - newPassword: newPassword - perr: perr]) + inDomain: domain + oldPassword: oldPassword + newPassword: newPassword + perr: perr]) { didChange = YES; @@ -553,6 +566,7 @@ // - (void) _fillContactInfosForUser: (NSMutableDictionary *) currentUser withUIDorEmail: (NSString *) uid + inDomain: (NSString *) domain { NSMutableArray *emails; NSDictionary *userEntry; @@ -574,7 +588,7 @@ [currentUser setObject: [NSNumber numberWithBool: YES] forKey: @"MailAccess"]; - sogoSources = [[self authenticationSourceIDsInDomain: nil] + sogoSources = [[self authenticationSourceIDsInDomain: domain] objectEnumerator]; while ((sourceID = [sogoSources nextObject])) { @@ -642,12 +656,17 @@ // associated with email addresses. // - (void) _retainUser: (NSDictionary *) newUser + withLogin: (NSString *) login { NSEnumerator *emails; NSString *key; + [[SOGoCache sharedCache] + setUserAttributes: [newUser jsonRepresentation] + forLogin: login]; + key = [newUser objectForKey: @"c_uid"]; - if (key) + if (key && ![key isEqualToString: login]) [[SOGoCache sharedCache] setUserAttributes: [newUser jsonRepresentation] forLogin: key]; @@ -680,10 +699,49 @@ return user; } +/** + * + * @see [SOGoUser initWithLogin:roles:trust:] + */ - (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid +{ + NSRange r; + NSString *username, *domain; + NSDictionary *infos; + SOGoSystemDefaults *sd; + + domain = nil; + infos = nil; + + r = [uid rangeOfString: @"@" options: NSBackwardsSearch]; + if (r.location != NSNotFound) + { + // The domain is probably appended to the username; + // make sure it is a defined domain in the configuration. + sd = [SOGoSystemDefaults sharedSystemDefaults]; + domain = [uid substringFromIndex: (r.location + r.length)]; + if ([[sd domainIds] containsObject: domain]) + username = [uid substringToIndex: r.location]; + else + domain = nil; + } + if (domain != nil) + infos = [self contactInfosForUserWithUIDorEmail: username + inDomain: domain]; + if (infos == nil) + // If the user was not found using the domain or if no domain was detected, + // search using the original uid. + infos = [self contactInfosForUserWithUIDorEmail: uid + inDomain: nil]; + + return infos; +} + +- (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid + inDomain: (NSString *) domain { NSMutableDictionary *currentUser; - NSString *aUID, *jsonUser; + NSString *aUID, *cacheUid, *jsonUser; BOOL newUser; if ([uid isEqualToString: @"anonymous"]) @@ -692,7 +750,11 @@ { // Remove the "@" prefix used to identified groups in the ACL tables. aUID = [uid hasPrefix: @"@"] ? [uid substringFromIndex: 1] : uid; - jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: aUID]; + if (domain) + cacheUid = [NSString stringWithFormat: @"%@@%@", aUID, domain]; + else + cacheUid = aUID; + jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: cacheUid]; currentUser = [jsonUser objectFromJSONString]; if (!([currentUser objectForKey: @"emails"] && [currentUser objectForKey: @"cn"])) @@ -711,11 +773,13 @@ else newUser = NO; [self _fillContactInfosForUser: currentUser - withUIDorEmail: aUID]; + withUIDorEmail: aUID + inDomain: domain]; if (newUser) { if ([[currentUser objectForKey: @"c_uid"] length] > 0) - [self _retainUser: currentUser]; + [self _retainUser: currentUser + withLogin: cacheUid]; else currentUser = nil; } diff --git a/SoObjects/SOGo/SOGoWebAuthenticator.h b/SoObjects/SOGo/SOGoWebAuthenticator.h index 804dd1650..c47b626de 100644 --- a/SoObjects/SOGo/SOGoWebAuthenticator.h +++ b/SoObjects/SOGo/SOGoWebAuthenticator.h @@ -1,9 +1,10 @@ /* SOGoWebAuthenticator.h - this file is part of SOGo * - * Copyright (C) 2007-2010 Inverse inc. + * Copyright (C) 2007-2011 Inverse inc. * * Author: Wolfgang Sourdeau * Ludovic Marcotte + * Francis Lachapelle * * 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 @@ -39,6 +40,7 @@ - (BOOL) checkLogin: (NSString *) _login password: (NSString *) _pwd + domain: (NSString *) _domain perr: (SOGoPasswordPolicyError *) _perr expire: (int *) _expire grace: (int *) _grace; diff --git a/SoObjects/SOGo/SOGoWebAuthenticator.m b/SoObjects/SOGo/SOGoWebAuthenticator.m index 8cd998c6e..0cb65c47b 100644 --- a/SoObjects/SOGo/SOGoWebAuthenticator.m +++ b/SoObjects/SOGo/SOGoWebAuthenticator.m @@ -3,6 +3,7 @@ * Copyright (C) 2007-2011 Inverse inc. * * Author: Wolfgang Sourdeau + * Francis Lachapelle * * 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 @@ -64,7 +65,7 @@ - (BOOL) checkLogin: (NSString *) _login password: (NSString *) _pwd { - NSString *username, *password, *value; + NSString *username, *domain, *password, *value; SOGoPasswordPolicyError perr; int expire, grace; @@ -83,19 +84,22 @@ return NO; [SOGoSession decodeValue: value - usingKey: _login - login: &username - password: &password]; + usingKey: _login + login: &username + domain: &domain + password: &password]; return [self checkLogin: username - password: password - perr: &perr - expire: &expire - grace: &grace]; + password: password + domain: domain + perr: &perr + expire: &expire + grace: &grace]; } - (BOOL) checkLogin: (NSString *) _login password: (NSString *) _pwd + domain: (NSString *) _domain perr: (SOGoPasswordPolicyError *) _perr expire: (int *) _expire grace: (int *) _grace @@ -116,10 +120,11 @@ } else rc = [[SOGoUserManager sharedUserManager] checkLogin: _login - password: _pwd - perr: _perr - expire: _expire - grace: _grace]; + password: _pwd + domain: _domain + perr: _perr + expire: _expire + grace: _grace]; //[self logWithFormat: @"Checked login with ppolicy enabled: %d %d %d", *_perr, *_expire, *_grace]; @@ -159,12 +164,13 @@ creds = [self parseCredentials: auth]; if ([creds count] > 1) { - NSString *login; + NSString *login, *domain; [SOGoSession decodeValue: [SOGoSession valueForSessionKey: [creds objectAtIndex: 1]] - usingKey: [creds objectAtIndex: 0] - login: &login - password: &password]; + usingKey: [creds objectAtIndex: 0] + login: &login + domain: &domain + password: &password]; } else password = nil; @@ -178,7 +184,7 @@ // - (NSString *) checkCredentials: (NSString *)_creds { - NSString *login, *pwd, *userKey, *sessionKey; + NSString *login, *domain, *pwd, *userKey, *sessionKey; NSArray *creds; SOGoPasswordPolicyError perr; @@ -194,17 +200,22 @@ sessionKey = [creds objectAtIndex:1]; [SOGoSession decodeValue: [SOGoSession valueForSessionKey: sessionKey] - usingKey: userKey - login: &login - password: &pwd]; + usingKey: userKey + login: &login + domain: &domain + password: &pwd]; if (![self checkLogin: login - password: pwd - perr: &perr - expire: &expire - grace: &grace]) + password: pwd + domain: domain + perr: &perr + expire: &expire + grace: &grace]) return nil; + if (domain) + login = [NSString stringWithFormat: @"%@@%@", login, domain]; + return login; } diff --git a/SoObjects/SOGo/SQLSource.m b/SoObjects/SOGo/SQLSource.m index 62083fe0b..4b9f6396d 100644 --- a/SoObjects/SOGo/SQLSource.m +++ b/SoObjects/SOGo/SQLSource.m @@ -28,6 +28,7 @@ #import #import +#import #import #import @@ -464,7 +465,7 @@ // We check if we should use a different login for IMAP if (_imapLoginField) { - if ([response objectForKey: _imapLoginField]) + if ([[response objectForKey: _imapLoginField] isNotNull]) [response setObject: [response objectForKey: _imapLoginField] forKey: @"c_imaplogin"]; } diff --git a/UI/MainUI/English.lproj/Localizable.strings b/UI/MainUI/English.lproj/Localizable.strings index aa9544710..695943de0 100644 --- a/UI/MainUI/English.lproj/Localizable.strings +++ b/UI/MainUI/English.lproj/Localizable.strings @@ -4,6 +4,7 @@ "Username:" = "Username:"; "Password:" = "Password:"; +"Domain:" = "Domain:"; "Connect" = "Connect"; diff --git a/UI/MainUI/French.lproj/Localizable.strings b/UI/MainUI/French.lproj/Localizable.strings index 4f1a50359..43b7a3fcf 100644 --- a/UI/MainUI/French.lproj/Localizable.strings +++ b/UI/MainUI/French.lproj/Localizable.strings @@ -4,6 +4,7 @@ "Username:" = "Nom d'utilisateur :"; "Password:" = "Mot de passe :"; +"Domain:" = "Domaine :"; "Connect" = "Connexion"; diff --git a/UI/MainUI/SOGoRootPage.m b/UI/MainUI/SOGoRootPage.m index 7abff33f1..7fd46cbfd 100644 --- a/UI/MainUI/SOGoRootPage.m +++ b/UI/MainUI/SOGoRootPage.m @@ -1,6 +1,6 @@ /* - Copyright (C) 2006-2010 Inverse inc. + Copyright (C) 2006-2011 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG This file is part of SOGo. @@ -149,7 +149,7 @@ SOGoAppointmentFolders *calendars; SOGoUserDefaults *ud; SOGoUser *loggedInUser; - NSString *username, *password, *language; + NSString *username, *password, *language, *domain; NSArray *supportedLanguages; SOGoPasswordPolicyError err; @@ -165,8 +165,9 @@ username = [request formValueForKey: @"userName"]; password = [request formValueForKey: @"password"]; language = [request formValueForKey: @"language"]; + domain = [request formValueForKey: @"domain"]; - if ((b = [auth checkLogin: username password: password + if ((b = [auth checkLogin: username password: password domain: domain perr: &err expire: &expire grace: &grace]) && (err == PolicyNoError) // no password policy @@ -178,16 +179,18 @@ [self logWithFormat: @"successful login for user '%@' - expire = %d grace = %d", username, expire, grace]; - json = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt: expire], @"expire", - [NSNumber numberWithInt: grace], @"grace", nil]; + [NSNumber numberWithInt: grace], @"grace", nil]; response = [self responseWithStatus: 200 andJSONRepresentation: json]; + if ([domain isNotNull]) + username = [NSString stringWithFormat: @"%@@%@", username, domain]; + authCookie = [self _cookieWithUsername: username - andPassword: password - forAuthenticator: auth]; + andPassword: password + forAuthenticator: auth]; [response addCookie: authCookie]; supportedLanguages = [[SOGoSystemDefaults sharedSystemDefaults] @@ -376,6 +379,16 @@ return ([[self loginSuffix] length]); } +- (NSArray *) loginDomains +{ + return [[SOGoSystemDefaults sharedSystemDefaults] loginDomains]; +} + +- (BOOL) hasLoginDomains +{ + return ([[self loginDomains] count] > 0); +} + - (void) setItem: (id) _item { ASSIGN (item, _item); @@ -414,7 +427,7 @@ - (WOResponse *) changePasswordAction { - NSString *username, *password, *newPassword, *value; + NSString *username, *domain, *password, *newPassword, *value; SOGoUserManager *um; SOGoPasswordPolicyError error; WOResponse *response; @@ -434,9 +447,10 @@ creds = [auth parseCredentials: value]; [SOGoSession decodeValue: [SOGoSession valueForSessionKey: [creds objectAtIndex: 1]] - usingKey: [creds objectAtIndex: 0] - login: &username - password: &password]; + usingKey: [creds objectAtIndex: 0] + login: &username + domain: &domain + password: &password]; newPassword = [message objectForKey: @"newPassword"]; @@ -444,13 +458,17 @@ // This will also update the cached password in memcached. if ([um changePasswordForLogin: username - oldPassword: password - newPassword: newPassword - perr: &error]) + inDomain: domain + oldPassword: password + newPassword: newPassword + perr: &error]) { // We delete the previous session [SOGoSession deleteValueForSessionKey: [creds objectAtIndex: 1]]; + if ([domain isNotNull]) + username = [NSString stringWithFormat: @"%@@%@", username, domain]; + response = [self responseWith204]; authCookie = [self _cookieWithUsername: username andPassword: newPassword diff --git a/UI/MainUI/SOGoUserHomePage.m b/UI/MainUI/SOGoUserHomePage.m index 56d36d5bf..b496d15f2 100644 --- a/UI/MainUI/SOGoUserHomePage.m +++ b/UI/MainUI/SOGoUserHomePage.m @@ -317,7 +317,8 @@ return response; } -- (WOResponse *) _usersResponseForResults: (NSArray *) users +- (NSMutableArray *) _usersForResults: (NSArray *) users + inDomain: (NSString *) domain { NSString *uid; NSDictionary *contact; @@ -325,8 +326,10 @@ NSMutableArray *jsonResponse, *jsonLine; NSArray *allUsers; int count, max; + BOOL activeUserIsInDomain; login = [[context activeUser] login]; + activeUserIsInDomain = (domain == nil || [[[context activeUser] domain] isEqualToString: domain]); // We sort our array - this is pretty useful for the Web // interface of SOGo. @@ -341,9 +344,11 @@ uid = [contact objectForKey: @"c_uid"]; // We do NOT return the current authenticated user - if (![uid isEqualToString: login]) + if (!activeUserIsInDomain || ![uid isEqualToString: login]) { jsonLine = [NSMutableArray arrayWithCapacity: 4]; + if (domain) + uid = [NSString stringWithFormat: @"%@@%@", uid, domain]; [jsonLine addObject: uid]; [jsonLine addObject: [contact objectForKey: @"cn"]]; [jsonLine addObject: [contact objectForKey: @"c_email"]]; @@ -354,24 +359,41 @@ } } - return [self responseWithStatus: 200 - andJSONRepresentation: jsonResponse]; + return jsonResponse; } - (id ) usersSearchAction { + NSMutableArray *users; + NSArray *currentUsers; NSString *contact, *domain; + NSEnumerator *visibleDomains; id result; SOGoUserManager *um; + SOGoSystemDefaults *sd; um = [SOGoUserManager sharedUserManager]; contact = [self queryParameterForKey: @"search"]; if ([contact length]) { domain = [[context activeUser] domain]; - result - = [self _usersResponseForResults: [um fetchUsersMatching: contact - inDomain: domain]]; + users = [self _usersForResults: [um fetchUsersMatching: contact + inDomain: domain] + inDomain: domain]; + if (domain) + { + // Add results from visible domains + sd = [SOGoSystemDefaults sharedSystemDefaults]; + visibleDomains = [[sd visibleDomainsForDomain: domain] objectEnumerator]; + while ((domain = [visibleDomains nextObject])) + { + currentUsers = [self _usersForResults: [um fetchUsersMatching: contact + inDomain: domain] + inDomain: domain]; + [users addObjectsFromArray: currentUsers]; + } + } + result = [self responseWithStatus: 200 andJSONRepresentation: users]; } else result = [NSException exceptionWithHTTPStatus: 400 diff --git a/UI/Templates/MainUI/SOGoRootPage.wox b/UI/Templates/MainUI/SOGoRootPage.wox index 3b14089b7..70060a4ac 100644 --- a/UI/Templates/MainUI/SOGoRootPage.wox +++ b/UI/Templates/MainUI/SOGoRootPage.wox @@ -51,6 +51,16 @@ string="languageText" label:noSelectionString="choose" /> + + +