diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index d50ac05f2..a29ebb356 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -435,48 +435,22 @@ static Class NSNullK; grace: (int *) grace { NSObject *sogoSource; - SOGoSystemDefaults *sd; NSEnumerator *authIDs; NSString *currentID; - BOOL checkOK; - NSRange r; - sd = [SOGoSystemDefaults sharedSystemDefaults]; checkOK = NO; authIDs = [[self authenticationSourceIDsInDomain: *domain] objectEnumerator]; while (!checkOK && (currentID = [authIDs nextObject])) { sogoSource = [_sources objectForKey: currentID]; - r = [login rangeOfString: [NSString stringWithFormat: @"@%@", *domain]]; checkOK = [sogoSource checkLogin: login password: password perr: perr expire: expire grace: grace]; - - // If we are using multidomain and the UIDFieldName is not part of the email address - // we must also try to bind without the domain part since internally, SOGo will use - // UIDFieldName @ domain as its unique identifier if the UIDFieldName is used to - // authenticate. This can happen for example of one has in LDAP: - // - // dn: uid=foo,dc=example,dc=com - // uid: foo - // mail: broccoli@example.com - // - // and authenticates with "foo", using bindFields = (uid, mail) and SOGoEnableDomainBasedUID = YES; - // The -checkLogin:... above would have failed because SOGo would first try to bind using: foo@example.com - // - if (!checkOK && *domain && [sd enableDomainBasedUID] && r.location != NSNotFound) - { - checkOK = [sogoSource checkLogin: [login substringToIndex: r.location] - password: password - perr: perr - expire: expire - grace: grace]; - } } if (checkOK && *domain == nil) @@ -582,6 +556,28 @@ static Class NSNullK; // authentication source and try to validate there, then cache it. jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: username]; currentUser = [jsonUser objectFromJSONString]; + + // + // If we are using multidomain and the UIDFieldName is not part of the email address + // we must bind without the domain part since internally, SOGo will use + // UIDFieldName @ domain as its unique identifier if the UIDFieldName is used to + // authenticate. This can happen for example of one has in LDAP: + // + // dn: uid=foo,dc=example,dc=com + // uid: foo + // mail: broccoli@example.com + // + // and authenticates with "foo", using bindFields = (uid, mail) and SOGoEnableDomainBasedUID = YES; + // Otherwise, -_sourceCheckLogin:... would have failed because SOGo would to bind using: foo@example.com + // + if ([[currentUser objectForKey: @"DomainLessLogin"] boolValue]) + { + NSRange r; + + r = [_login rangeOfString: [NSString stringWithFormat: @"@%@", *_domain]]; + _login = [_login substringToIndex: r.location]; + } + dictPassword = [currentUser objectForKey: @"password"]; if (useCache && currentUser && dictPassword) { @@ -602,11 +598,16 @@ static Class NSNullK; } // Before caching user attributes, we must check if SOGoEnableDomainBasedUID is enabled - // but we don't have a domain. That would happen for example if a user authenticates - // without the domain part. + // but we don't have a domain. That would happen for example if the user authenticates + // without the domain part. We must also cache that information, since SOGo will try + // afterward to bind with UIDFieldName@domain, and it could potentially not exist + // in the authentication source. See the rationale in _sourceCheckLogin: ... if ([sd enableDomainBasedUID] && [username rangeOfString: @"@"].location == NSNotFound) - username = [NSString stringWithFormat: @"%@@%@", username, *_domain]; + { + username = [NSString stringWithFormat: @"%@@%@", username, *_domain]; + [currentUser setObject: [NSNumber numberWithBool: YES] forKey: @"DomainLessLogin"]; + } // It's important to cache the password here as we might have cached the // user's entry in -contactInfosForUserWithUIDorEmail: and if we don't @@ -729,9 +730,9 @@ static Class NSNullK; // // // -- (void) _fillContactInfosForUser: (NSMutableDictionary *) currentUser - withUIDorEmail: (NSString *) uid - inDomain: (NSString *) domain +- (void) _fillContactInfosForUser: (NSMutableDictionary *) theCurrentUser + withUIDorEmail: (NSString *) theUID + inDomain: (NSString *) theDomain { NSString *sourceID, *cn, *c_domain, *c_uid, *c_imaphostname, *c_imaplogin, *c_sievehostname; NSObject *currentSource; @@ -750,21 +751,31 @@ static Class NSNullK; c_imaplogin = nil; c_sievehostname = nil; - [currentUser setObject: [NSNumber numberWithBool: YES] - forKey: @"CalendarAccess"]; - [currentUser setObject: [NSNumber numberWithBool: YES] - forKey: @"MailAccess"]; + [theCurrentUser setObject: [NSNumber numberWithBool: YES] + forKey: @"CalendarAccess"]; + [theCurrentUser setObject: [NSNumber numberWithBool: YES] + forKey: @"MailAccess"]; - sogoSources = [[self authenticationSourceIDsInDomain: domain] objectEnumerator]; + if ([[theCurrentUser objectForKey: @"DomainLessLogin"] boolValue]) + { + NSRange r; + + r = [theUID rangeOfString: [NSString stringWithFormat: @"@%@", theDomain]]; + theUID = [theUID substringToIndex: r.location]; + } + + + sogoSources = [[self authenticationSourceIDsInDomain: theDomain] objectEnumerator]; userEntry = nil; while (!userEntry && (sourceID = [sogoSources nextObject])) { currentSource = [_sources objectForKey: sourceID]; - userEntry = [currentSource lookupContactEntryWithUIDorEmail: uid - inDomain: domain]; + + userEntry = [currentSource lookupContactEntryWithUIDorEmail: theUID + inDomain: theDomain]; if (userEntry) { - [currentUser setObject: sourceID forKey: @"SOGoSource"]; + [theCurrentUser setObject: sourceID forKey: @"SOGoSource"]; if (!cn) cn = [userEntry objectForKey: @"c_cn"]; if (!c_uid) @@ -782,30 +793,30 @@ static Class NSNullK; c_sievehostname = [userEntry objectForKey: @"c_sievehostname"]; access = [[userEntry objectForKey: @"CalendarAccess"] boolValue]; if (!access) - [currentUser setObject: [NSNumber numberWithBool: NO] - forKey: @"CalendarAccess"]; + [theCurrentUser setObject: [NSNumber numberWithBool: NO] + forKey: @"CalendarAccess"]; access = [[userEntry objectForKey: @"MailAccess"] boolValue]; if (!access) - [currentUser setObject: [NSNumber numberWithBool: NO] - forKey: @"MailAccess"]; + [theCurrentUser setObject: [NSNumber numberWithBool: NO] + forKey: @"MailAccess"]; // We check if it's a group isGroup = [userEntry objectForKey: @"isGroup"]; if (isGroup) - [currentUser setObject: isGroup forKey: @"isGroup"]; + [theCurrentUser setObject: isGroup forKey: @"isGroup"]; // We also fill the resource attributes, if any if ([userEntry objectForKey: @"isResource"]) - [currentUser setObject: [userEntry objectForKey: @"isResource"] - forKey: @"isResource"]; + [theCurrentUser setObject: [userEntry objectForKey: @"isResource"] + forKey: @"isResource"]; if ([userEntry objectForKey: @"numberOfSimultaneousBookings"]) - [currentUser setObject: [userEntry objectForKey: @"numberOfSimultaneousBookings"] - forKey: @"numberOfSimultaneousBookings"]; + [theCurrentUser setObject: [userEntry objectForKey: @"numberOfSimultaneousBookings"] + forKey: @"numberOfSimultaneousBookings"]; // This is Active Directory specific attribute (needed on OpenChange/* layer) if ([userEntry objectForKey: @"samaccountname"]) - [currentUser setObject: [userEntry objectForKey: @"samaccountname"] - forKey: @"sAMAccountName"]; + [theCurrentUser setObject: [userEntry objectForKey: @"samaccountname"] + forKey: @"sAMAccountName"]; } } @@ -817,20 +828,20 @@ static Class NSNullK; c_domain = @""; if (c_imaphostname) - [currentUser setObject: c_imaphostname forKey: @"c_imaphostname"]; + [theCurrentUser setObject: c_imaphostname forKey: @"c_imaphostname"]; if (c_imaplogin) - [currentUser setObject: c_imaplogin forKey: @"c_imaplogin"]; + [theCurrentUser setObject: c_imaplogin forKey: @"c_imaplogin"]; if (c_sievehostname) - [currentUser setObject: c_sievehostname forKey: @"c_sievehostname"]; + [theCurrentUser setObject: c_sievehostname forKey: @"c_sievehostname"]; - [currentUser setObject: emails forKey: @"emails"]; - [currentUser setObject: cn forKey: @"cn"]; - [currentUser setObject: c_uid forKey: @"c_uid"]; - [currentUser setObject: c_domain forKey: @"c_domain"]; + [theCurrentUser setObject: emails forKey: @"emails"]; + [theCurrentUser setObject: cn forKey: @"cn"]; + [theCurrentUser setObject: c_uid forKey: @"c_uid"]; + [theCurrentUser setObject: c_domain forKey: @"c_domain"]; // If our LDAP queries gave us nothing, we add at least one default // email address based on the default domain. - [self _fillContactMailRecords: currentUser]; + [self _fillContactMailRecords: theCurrentUser]; } // @@ -939,8 +950,10 @@ static Class NSNullK; cacheUid = [NSString stringWithFormat: @"%@@%@", aUID, domain]; else cacheUid = aUID; + jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: cacheUid]; currentUser = [jsonUser objectFromJSONString]; + if ([currentUser isKindOfClass: NSNullK]) currentUser = nil; else if (!([currentUser objectForKey: @"emails"] @@ -950,8 +963,10 @@ static Class NSNullK; // that we have an occurence with only a cached password. In the // latter case, we update the entry with the remaining information // and recache the value. - if (!currentUser || ([currentUser count] == 1 && [currentUser objectForKey: @"password"])) - { + if (!currentUser || + ([currentUser count] == 1 && [currentUser objectForKey: @"password"]) || + ([currentUser count] == 2 && [currentUser objectForKey: @"password"] && [currentUser objectForKey: @"DomainLessLogin"])) + { newUser = YES; if (!currentUser) @@ -976,9 +991,13 @@ static Class NSNullK; sd = [SOGoSystemDefaults sharedSystemDefaults]; - // to true but we don't have a domain part. + // SOGoEnableDomainBasedUID is set to YES but we don't have a domain part. This happens in + // multi-domain environments authenticating only with the UIDFieldName if ([sd enableDomainBasedUID] && !domain) - cacheUid = [NSString stringWithFormat: @"%@@%@", cacheUid, [currentUser objectForKey: @"c_domain"]]; + { + cacheUid = [NSString stringWithFormat: @"%@@%@", cacheUid, [currentUser objectForKey: @"c_domain"]]; + [currentUser setObject: [NSNumber numberWithBool: YES] forKey: @"DomainLessLogin"]; + } [self _retainUser: currentUser withLogin: cacheUid]; }