See ChangeLog

Monotone-Parent: 024380d579a482e49866c36ef54561cf8b39ab02
Monotone-Revision: 2cbc46c16f9b3d45d868b15a968f614dbbaf9749

Monotone-Author: ludovic@Sophos.ca
Monotone-Date: 2010-03-08T15:18:05
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Ludovic Marcotte 2010-03-08 15:18:05 +00:00
parent 13206f066d
commit 40c3cb74d0
26 changed files with 1059 additions and 98 deletions

View File

@ -1,3 +1,16 @@
2010-03-08 Ludovic Marcotte <lmarcotte@inverse.ca>
* Added SoObjects/SOGo/SOGoConstants.{h,m} - a new file
that will hold eventually all SOGo constants.
* Reworked the authentication code to use a generic
method for password authentication (LDAP + SQL).
* Added password change support
* Updated the SOPE patchset which now includes
password-policy support.
2010-03-05 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/SOGo/NSString+DAV.m (-davSetterName): enhanced so that

View File

@ -1,3 +1,311 @@
Index: sope-ldap/NGLdap/NGLdapConnection.m
===================================================================
--- sope-ldap/NGLdap/NGLdapConnection.m (revision 1664)
+++ sope-ldap/NGLdap/NGLdapConnection.m (working copy)
@@ -26,7 +26,6 @@
#include "NGLdapModification.h"
#include "EOQualifier+LDAP.h"
#include "common.h"
-#include <ldap.h>
static BOOL LDAPDebugEnabled = NO;
static BOOL LDAPInitialBindSpecific = NO;
@@ -310,6 +309,295 @@
return NO;
}
+#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
+- (BOOL) bindWithMethod: (NSString *) _method
+ binddn: (NSString *) _login
+ credentials: (NSString *) _cred
+ perr: (LDAPPasswordPolicyError *) _perr
+ expire: (int *) _expire
+ grace: (int *) _grace
+{
+ LDAPControl **sctrlsp = NULL;
+ LDAPControl *sctrls[2];
+ LDAPControl sctrl[2];
+ LDAPControl **ctrls;
+ LDAPControl c, *ctrl;
+ LDAPMessage *result = NULL;
+
+
+ int err, msgid, rc;
+ const char *l, *p;
+ char *matched = NULL;
+ char *info = NULL;
+ char **refs = NULL;
+ struct berval passwd = { 0, NULL };
+
+ l = (char *)[_login UTF8String];
+ p = LDAPUseLatin1Creds
+ ? (char *)[_cred cString]
+ : (char *)[_cred UTF8String];
+
+ *_perr = -1;
+ passwd.bv_val = p;
+ passwd.bv_len = strlen(p);
+
+
+ c.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
+ c.ldctl_value.bv_val = NULL;
+ c.ldctl_value.bv_len = 0;
+ c.ldctl_iscritical = 0;
+ sctrl[0] = c;
+ sctrls[0] = &sctrl[0];
+ sctrls[1] = NULL;
+
+ sctrlsp = sctrls;
+
+ rc = ldap_sasl_bind(self->handle, l, LDAP_SASL_SIMPLE, &passwd, sctrlsp, NULL, &msgid);
+
+ if (msgid == -1 || rc != LDAP_SUCCESS)
+ {
+ [self logWithFormat: @"bind - ldap_sasl_bind call failed"];
+ return NO;
+ }
+
+ rc = ldap_result(self->handle, msgid, LDAP_MSG_ALL, NULL, &result);
+
+ if (rc == -1)
+ {
+ [self logWithFormat: @"bind - ldap_result call failed"];
+ if (result) ldap_msgfree(result);
+ return NO;
+ }
+
+ [self logWithFormat: @"bind - ldap_result call result: %d", rc];
+
+ rc = ldap_parse_result(self->handle, result, &err, &matched, &info, &refs, &ctrls, 1);
+
+ if (rc != LDAP_SUCCESS)
+ {
+ [self logWithFormat: @"bind - ldap_parse_result call failed"];
+ //if (result) ldap_msgfree(result); => causes a crash!?
+ if (matched) ber_memfree(matched);
+ if (info) ber_memfree(info);
+ if (refs) ber_memvfree((void **)refs);
+ return NO;
+ }
+
+ if (err == LDAP_SUCCESS)
+ self->flags.isBound = YES;
+ else
+ self->flags.isBound = NO;
+
+ // Even if we aren't bound to the server, we continue and we go get the
+ // policy control
+ if (ctrls)
+ {
+ ctrl = ldap_control_find(LDAP_CONTROL_PASSWORDPOLICYRESPONSE, ctrls, NULL);
+ if (ctrl)
+ {
+ rc = ldap_parse_passwordpolicy_control(self->handle, ctrl, _expire, _grace, _perr);
+
+ if (rc == LDAP_SUCCESS)
+ {
+ [self logWithFormat: @"bind - policy values: %d %d %d - bound: %d", *_expire, *_grace, *_perr, self->flags.isBound];
+ }
+ else
+ [self logWithFormat: @"bind - ldap_parse_passwordpolicy call failed"];
+ }
+ else
+ [self logWithFormat: @"bind - ldap_control_find call failed"];
+
+ ldap_controls_free(ctrls);
+ }
+ else
+ {
+ [self logWithFormat: @"bind - ldap_parse_result - ctrls is NULL"];
+ }
+
+ return self->flags.isBound;
+}
+
+//
+// No need to bind prior to calling this method. In fact,
+// if a bind() was issued prior calling this method, it
+// will fail.
+//
+- (BOOL) changePasswordAtDn: (NSString *) _dn
+ oldPassword: (NSString *) _oldPassword
+ newPassword: (NSString *) _newPassword
+ perr: (LDAPPasswordPolicyError *) _perr
+
+{
+ const char *user, *p;
+ int rc;
+
+ *_perr = -1;
+
+ user = (char *)[_dn UTF8String];
+ p = LDAPUseLatin1Creds ? (char *)[_oldPassword cString] : (char *)[_oldPassword UTF8String];
+
+ if (!self->flags.isBound)
+ {
+ rc = ldap_simple_bind_s(self->handle, user, p);
+
+ if (rc == LDAP_SUCCESS)
+ {
+ struct berval newpw = { 0, NULL };
+ struct berval oldpw = { 0, NULL };
+ struct berval bv = {0, NULL};
+ struct berval *retdata = NULL;
+
+ LDAPControl *sctrls[2];
+ LDAPControl **ctrls;
+ LDAPControl sctrl[2];
+ LDAPControl c, *ctrl;
+ LDAPMessage *result;
+
+ BerElement *ber = NULL;
+
+ char *matcheddn = NULL, *retoid = NULL, *text = NULL, **refs = NULL;
+ int idd, grace, expire, code;
+
+ self->flags.isBound = YES;
+ code = LDAP_OTHER;
+
+ newpw.bv_val = LDAPUseLatin1Creds ? (char *)[_newPassword cString] : (char *)[_newPassword UTF8String];
+ newpw.bv_len = strlen(newpw.bv_val);
+
+ oldpw.bv_val = p;
+ oldpw.bv_len = strlen(p);
+
+ ber = ber_alloc_t(LBER_USE_DER);
+
+ if (ber == NULL)
+ return NO;
+
+ ber_printf(ber, "{" /*}*/ );
+ ber_printf(ber, "ts", LDAP_TAG_EXOP_MODIFY_PASSWD_ID, user);
+ ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, &oldpw);
+ ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, &newpw);
+ ber_printf(ber, /*{*/ "N}" );
+
+ rc = ber_flatten2(ber, &bv, 0 );
+
+ if (rc < 0)
+ {
+ [self logWithFormat: @"change password - ber_flatten2 call failed"];
+ ber_free(ber, 1);
+ return NO;
+ }
+
+ // Everything is alright...
+ *_perr = -1;
+
+ c.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
+ c.ldctl_value.bv_val = NULL;
+ c.ldctl_value.bv_len = 0;
+ c.ldctl_iscritical = 0;
+ sctrl[0] = c;
+ sctrls[0] = &sctrl[0];
+ sctrls[1] = NULL;
+
+ rc = ldap_set_option(self->handle, LDAP_OPT_SERVER_CONTROLS, sctrls);
+
+ if (rc != LDAP_OPT_SUCCESS)
+ {
+ [self logWithFormat: @"change password - ldap_set_option call failed"];
+ ber_free(ber, 1);
+ return NO;
+ }
+
+ rc = ldap_extended_operation(self->handle,
+ LDAP_EXOP_MODIFY_PASSWD, &bv,
+ NULL, NULL, &idd);
+
+ ber_free(ber, 1);
+
+ if (rc != LDAP_SUCCESS )
+ {
+ [self logWithFormat: @"change password - ldap_extended_operation call failed"];
+ return NO;
+ }
+
+ rc = ldap_result(self->handle, LDAP_RES_ANY, LDAP_MSG_ALL, NULL, &result);
+
+ if (rc < 0)
+ {
+ [self logWithFormat: @"change password - ldap_result call failed"];
+ return NO;
+ }
+
+ rc = ldap_parse_result(self->handle, result, &code, &matcheddn, &text, &refs, &ctrls, 0 );
+
+ if (rc != LDAP_SUCCESS)
+ {
+ [self logWithFormat: @"change password - ldap_parse_result call failed, rc = %d, code = %d, matcheddn = %s, text = %s", rc, code, matcheddn, text];
+ ber_memfree(text);
+ ber_memfree(matcheddn);
+ ber_memvfree((void **) refs);
+ free(ctrls);
+ return NO;
+ }
+
+ rc = ldap_parse_extended_result(self->handle, result, &retoid, &retdata, 1);
+ if (rc != LDAP_SUCCESS)
+ {
+ [self logWithFormat: @"change password - ldap_parse_extended result call failed"];
+ ber_memfree(text);
+ ber_memfree(matcheddn);
+ ber_memvfree((void **) refs);
+ ber_memfree(retoid);
+ ber_bvfree(retdata);
+ free(ctrls);
+ return NO;
+ }
+
+ ctrl = ldap_control_find(LDAP_CONTROL_PASSWORDPOLICYRESPONSE, ctrls, NULL);
+
+ if (ctrl)
+ {
+ rc = ldap_parse_passwordpolicy_control(self->handle, ctrl, &expire, &grace, _perr);
+
+ if (rc == LDAP_SUCCESS && *_perr == PP_noError)
+ {
+ [self logWithFormat: @"change password - policy values: %d %d %d", expire, grace, *_perr];
+ }
+ else
+ {
+ [self logWithFormat: @"change password - ldap_parse_passwordpolicy call failed or error during password change: %d", *_perr];
+ ber_memfree(text);
+ ber_memfree(matcheddn);
+ ber_memvfree((void **) refs);
+ ber_memfree(retoid);
+ ber_bvfree(retdata);
+ free(ctrls);
+ return NO;
+ }
+ }
+ else
+ {
+ // Ending up here doesn't mean that things failed. It could simply be caused by the
+ // fact that the password change was a success but no policy control object
+ // could be found.
+ [self logWithFormat: @"change password - ldap_control_find call failed"];
+ }
+
+ ber_memfree(text);
+ ber_memfree(matcheddn);
+ ber_memvfree((void **) refs);
+ ber_memfree(retoid);
+ ber_bvfree(retdata);
+ free(ctrls);
+
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+#endif
+
/* running queries */
- (void)setQueryTimeLimit:(NSTimeInterval)_timeLimit {
Index: sope-ldap/NGLdap/NGLdapEntry.m
===================================================================
--- sope-ldap/NGLdap/NGLdapEntry.m (revision 1664)
@ -25,7 +333,12 @@ Index: sope-ldap/NGLdap/ChangeLog
===================================================================
--- sope-ldap/NGLdap/ChangeLog (revision 1664)
+++ sope-ldap/NGLdap/ChangeLog (working copy)
@@ -1,3 +1,8 @@
@@ -1,3 +1,13 @@
+2010-03-08 Ludovic Marcotte <lmarcotte@inverse.ca>
+
+ * Added password policy support when binding to the
+ LDAP server or when changing passwords.
+
+2009-08-13 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * NGLdapEntry.m (-attributeWithName:): attribute names are now
@ -34,6 +347,41 @@ Index: sope-ldap/NGLdap/ChangeLog
2009-04-02 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* NGLdapConnection.m (useSSL,startTLS): new method enabling
Index: sope-ldap/NGLdap/NGLdapConnection.h
===================================================================
--- sope-ldap/NGLdap/NGLdapConnection.h (revision 1664)
+++ sope-ldap/NGLdap/NGLdapConnection.h (working copy)
@@ -25,6 +25,9 @@
#import <Foundation/NSObject.h>
#import <Foundation/NSDate.h>
+#define LDAP_DEPRECATED 1
+#include <ldap.h>
+
@class NSString, NSArray, NSEnumerator;
@class EOQualifier;
@class NGLdapEntry;
@@ -65,6 +68,20 @@
- (BOOL)bindWithMethod:(NSString *)_method
binddn:(NSString *)_login credentials:(NSString *)_cred;
+#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
+- (BOOL) bindWithMethod: (NSString *) _method
+ binddn: (NSString *) _login
+ credentials: (NSString *) _cred
+ perr: (LDAPPasswordPolicyError *) _perr
+ expire: (int *) _expire
+ grace: (int *) _grace;
+
+- (BOOL) changePasswordAtDn: (NSString *) _dn
+ oldPassword: (NSString *) _oldPassword
+ newPassword: (NSString *) _newPassword
+ perr: (LDAPPasswordPolicyError *) _perr;
+#endif
+
/* query parameters */
- (void)setQueryTimeLimit:(NSTimeInterval)_timeLimit;
Index: sope-gdl1/PostgreSQL/PostgreSQL72Channel.m
===================================================================
--- sope-gdl1/PostgreSQL/PostgreSQL72Channel.m (revision 1664)

View File

@ -19,6 +19,7 @@ SOGo_HEADER_FILES = \
SOGoProductLoader.h \
\
SOGoCache.h \
SOGoConstants.h \
SOGoObject.h \
SOGoContentObject.h \
SOGoFolder.h \
@ -80,6 +81,7 @@ SOGo_OBJC_FILES = \
SOGoProductLoader.m \
\
SOGoCache.m \
SOGoConstants.m \
SOGoObject.m \
SOGoContentObject.m \
SOGoFolder.m \

View File

@ -1,6 +1,6 @@
/* LDAPSource.h - this file is part of SOGo
*
* Copyright (C) 2007-2009 Inverse inc.
* Copyright (C) 2007-2010 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
@ -26,6 +26,7 @@
#import <Foundation/NSObject.h>
#include "SOGoSource.h"
#include "SOGoConstants.h"
@class NSDictionary;
@class NSString;
@ -60,6 +61,8 @@
NSDictionary *modulesConstraints;
NSMutableArray *searchAttributes;
BOOL passwordPolicy;
}
- (void) setBindDN: (NSString *) newBindDN

View File

@ -29,6 +29,7 @@
#import <NGLdap/NGLdapConnection.h>
#import <NGLdap/NGLdapAttribute.h>
#import <NGLdap/NGLdapEntry.h>
#import <NGLdap/NGLdapModification.h>
#import "NSArray+Utilities.h"
#import "NSString+Utilities.h"
@ -157,6 +158,7 @@ static NSArray *commonSearchFields;
_filter = nil;
searchAttributes = nil;
passwordPolicy = NO;
}
return self;
@ -239,6 +241,9 @@ static NSArray *commonSearchFields;
ASSIGN (_scope, ([udSource objectForKey: @"scope"]
? [udSource objectForKey: @"scope"]
: (id)@"sub"));
if ([udSource objectForKey: @"passwordPolicy"])
passwordPolicy = [[udSource objectForKey: @"passwordPolicy"] boolValue];
}
return self;
@ -414,16 +419,19 @@ static NSArray *commonSearchFields;
return userDN;
}
- (BOOL) checkLogin: (NSString *) loginToCheck
andPassword: (NSString *) passwordToCheck
- (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace
{
BOOL didBind;
NSString *userDN;
NGLdapConnection *bindConnection;
NSString *userDN;
BOOL didBind;
didBind = NO;
if ([loginToCheck length] > 0)
if ([_login length] > 0)
{
bindConnection = [[NGLdapConnection alloc] initWithHostName: hostname
port: port];
@ -432,16 +440,24 @@ static NSArray *commonSearchFields;
if (queryTimeout > 0)
[bindConnection setQueryTimeLimit: queryTimeout];
if (bindFields)
userDN = [self _fetchUserDNForLogin: loginToCheck];
userDN = [self _fetchUserDNForLogin: _login];
else
userDN = [NSString stringWithFormat: @"%@=%@,%@",
IDField, loginToCheck, baseDN];
IDField, _login, baseDN];
if (userDN)
{
NS_DURING
didBind = [bindConnection bindWithMethod: @"simple"
binddn: userDN
credentials: passwordToCheck];
if (!passwordPolicy)
didBind = [bindConnection bindWithMethod: @"simple"
binddn: userDN
credentials: _pwd];
else
didBind = [bindConnection bindWithMethod: @"simple"
binddn: userDN
credentials: _pwd
perr: (void *)_perr
expire: _expire
grace: _grace];
NS_HANDLER
;
NS_ENDHANDLER
@ -450,10 +466,79 @@ static NSArray *commonSearchFields;
}
[bindConnection release];
}
return didBind;
}
- (BOOL) changePasswordForLogin: (NSString *) login
oldPassword: (NSString *) oldPassword
newPassword: (NSString *) newPassword
perr: (SOGoPasswordPolicyError *) perr
{
NGLdapConnection *bindConnection;
NSString *userDN;
BOOL didChange;
didChange = NO;
if ([login length] > 0)
{
bindConnection = [[NGLdapConnection alloc] initWithHostName: hostname
port: port];
if (![encryption length] || [self _setupEncryption: bindConnection])
{
if (queryTimeout > 0)
[bindConnection setQueryTimeLimit: queryTimeout];
if (bindFields)
userDN = [self _fetchUserDNForLogin: login];
else
userDN = [NSString stringWithFormat: @"%@=%@,%@",
IDField, login, baseDN];
if (userDN)
{
NS_DURING
if (!passwordPolicy)
{
// We don't use a password policy - we simply use
// a modify-op to change the password
NGLdapModification *mod;
NGLdapAttribute *attr;
NSArray *changes;
attr = [[NGLdapAttribute alloc] initWithAttributeName: @"userPassword"];
[attr addStringValue: newPassword];
mod = [NGLdapModification replaceModification: attr];
changes = [NSArray arrayWithObject: mod];
perr = PolicyNoError;
if ([bindConnection bindWithMethod: @"simple"
binddn: userDN
credentials: oldPassword])
didChange = [bindConnection modifyEntryWithDN: userDN
changes: changes];
else
didChange = NO;
}
else
didChange = [bindConnection changePasswordAtDn: userDN
oldPassword: oldPassword
newPassword: newPassword
perr: (void *)perr];
NS_HANDLER
;
NS_ENDHANDLER
;
}
}
[bindConnection release];
}
return didChange;
}
/* contact management */
- (EOQualifier *) _qualifierForFilter: (NSString *) filter
{

View File

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

View File

@ -0,0 +1,48 @@
/* SOGoConstants.h - this file is part of SOGo
*
* Copyright (C) 2010 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 _SOGOCONSTANTS_H_
#define _SOGOCONSTANTS_H_
// This is a perfect copy of the OpenLDAP's
// LDAPPasswordPolicyError enum. We redeclare it
// so that we always include the ppolicy code
// within SOGo.
typedef enum
{
PolicyPasswordExpired = 0,
PolicyAccountLocked = 1,
PolicyChangeAfterReset = 2,
PolicyPasswordModNotAllowed = 3,
PolicyMustSupplyOldPassword = 4,
PolicyInsufficientPasswordQuality = 5,
PolicyPasswordTooShort = 6,
PolicyPasswordTooYoung = 7,
PolicyPasswordInHistory = 8,
PolicyNoError = 65535,
} SOGoPasswordPolicyError;
// Domain defaults
extern NSString *SOGoPasswordChangeEnabled;
extern NSString *SOGoPasswordPolicyEnabled;
#endif /* _SOGOCONSTANTS_H_ */

View File

@ -0,0 +1,27 @@
/* SOGoConstants.m - this file is part of SOGo
*
* Copyright (C) 2010 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/NSString.h>
// LDAP Password Policy Constants
NSString* SOGoPasswordChangeEnabled = @"SOGoPasswordChangeEnabled";
NSString* SOGoPasswordPolicyEnabled = @"SOGoPasswordPolicyEnabled";

View File

@ -27,6 +27,7 @@
#import <NGObjWeb/WOResponse.h>
#import <NGExtensions/NSObject+Logs.h>
#import "SOGoConstants.h"
#import "SOGoUserManager.h"
#import "SOGoPermissions.h"
#import "SOGoUser.h"
@ -48,8 +49,22 @@
- (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd
{
return [[SOGoUserManager sharedUserManager] checkLogin: _login
andPassword: _pwd];
SOGoPasswordPolicyError perr;
int expire, grace;
BOOL b;
perr = PolicyNoError;
b = [[SOGoUserManager sharedUserManager] checkLogin: _login
password: _pwd
perr: &perr
expire: &expire
grace: &grace];
if (b && perr == PolicyNoError)
return YES;
return NO;
}
- (NSString *) passwordInContext: (WOContext *) context

View File

@ -1,6 +1,6 @@
/* SOGoDomainDefaults.h - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
* Copyright (C) 2009-2010 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
@ -46,6 +46,8 @@
- (BOOL) sieveScriptsEnabled;
- (BOOL) forwardEnabled;
- (BOOL) vacationEnabled;
- (BOOL) passwordChangeEnabled;
- (BOOL) passwordPolicyEnabled;
- (NSString *) mailingMechanism;
- (NSString *) smtpServer;
- (NSString *) mailSpoolPath;

View File

@ -1,6 +1,6 @@
/* SOGoDomainDefaults.m - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
* Copyright (C) 2009-2010 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
@ -30,6 +30,7 @@
#import "SOGoSystemDefaults.h"
#import "SOGoDomainDefaults.h"
#import "SOGoConstants.h"
@implementation SOGoDomainDefaults
@ -170,6 +171,16 @@
return [self boolForKey: @"SOGoVacationEnabled"];
}
- (BOOL) passwordChangeEnabled
{
return [self boolForKey: SOGoPasswordChangeEnabled];
}
- (BOOL) passwordPolicyEnabled
{
return [self boolForKey: SOGoPasswordPolicyEnabled];
}
- (NSString *) mailingMechanism
{
NSString *mailingMechanism;

View File

@ -1,6 +1,6 @@
/* SOGoSource.h - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
* Copyright (C) 2009-2010 Inverse inc.
*
* Author: Ludovic Marcotte <lmarcotte@inverse.ca>
*
@ -25,6 +25,8 @@
#import <Foundation/NSObject.h>
#import "SOGoConstants.h"
@class NSDictionary;
@class NSString;
@ -38,8 +40,16 @@
- (NSString *) domain;
- (BOOL) checkLogin: (NSString *) login
andPassword: (NSString *) password;
- (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace;
- (BOOL) changePasswordForLogin: (NSString *) login
oldPassword: (NSString *) oldPassword
newPassword: (NSString *) newPassword
perr: (SOGoPasswordPolicyError *) perr;
- (NSDictionary *) lookupContactEntry: (NSString *) theID;
- (NSDictionary *) lookupContactEntryWithUIDorEmail: (NSString *) entryID;

View File

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

View File

@ -1,6 +1,6 @@
/* SOGoUserManager.h - this file is part of SOGo
*
* Copyright (C) 2007-2009 Inverse inc.
* Copyright (C) 2007-2010 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
@ -25,6 +25,8 @@
#import <Foundation/NSObject.h>
#import "SOGoConstants.h"
@class NSDictionary;
@class NSMutableDictionary;
@class NSString;
@ -75,9 +77,16 @@
- (NSString *) getUIDForEmail: (NSString *) email;
- (NSString *) getLoginForDN: (NSString *) theDN;
- (BOOL) checkLogin: (NSString *) login
andPassword: (NSString *) password;
- (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace;
- (BOOL) changePasswordForLogin: (NSString *) login
oldPassword: (NSString *) oldPassword
newPassword: (NSString *) newPassword
perr: (SOGoPasswordPolicyError *) perr;
@end
#endif /* SOGOUSERMANAGER_H */

View File

@ -1,6 +1,6 @@
/* SOGoUserManager.m - this file is part of SOGo
*
* Copyright (C) 2007-2009 Inverse inc.
* Copyright (C) 2007-2010 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
@ -36,6 +36,7 @@
#import "SOGoSystemDefaults.h"
#import "SOGoUserManager.h"
#import "SOGoCache.h"
#import "SOGoConstants.h"
#import "SOGoSource.h"
@implementation SOGoUserManagerRegistry
@ -89,7 +90,7 @@
{
NSString *sourceID, *value, *type;
NSMutableDictionary *metadata;
NSObject <SOGoSource> *ldapSource;
NSObject <SOGoSource> *sogoSource;
BOOL isAddressBook;
Class c;
@ -97,10 +98,10 @@
if ([sourceID length] > 0)
{
type = [[udSource objectForKey: @"type"] lowercaseString];
c = NSClassFromString ([_registry sourceClassForType: type]);
ldapSource = [c sourceFromUDSource: udSource inDomain: domain];
c = NSClassFromString([_registry sourceClassForType: type]);
sogoSource = [c sourceFromUDSource: udSource inDomain: domain];
if (sourceID)
[_sources setObject: ldapSource forKey: sourceID];
[_sources setObject: sogoSource forKey: sourceID];
else
[self errorWithFormat: @"id field missing in an user source,"
@" check the SOGoUserSources defaults"];
@ -335,45 +336,84 @@
{
NSDictionary *contactInfos;
// NSLog (@"getUIDForEmail: %@", email);
contactInfos = [self contactInfosForUserWithUIDorEmail: email];
return [contactInfos objectForKey: @"c_uid"];
}
- (BOOL) _sourceChangePasswordForLogin: (NSString *) login
oldPassword: (NSString *) oldPassword
newPassword: (NSString *) newPassword
perr: (SOGoPasswordPolicyError *) perr
{
NSObject <SOGoSource> *sogoSource;
NSEnumerator *authIDs;
NSString *currentID;
BOOL didChange;
didChange = NO;
authIDs = [[self authenticationSourceIDsInDomain: nil] objectEnumerator];
while (!didChange && (currentID = [authIDs nextObject]))
{
sogoSource = [_sources objectForKey: currentID];
didChange = [sogoSource changePasswordForLogin: login
oldPassword: oldPassword
newPassword: newPassword
perr: perr];
}
return didChange;
}
- (BOOL) _sourceCheckLogin: (NSString *) login
andPassword: (NSString *) password
{
NSObject <SOGoSource> *ldapSource;
perr: (SOGoPasswordPolicyError *) perr
expire: (int *) expire
grace: (int *) grace
{
NSObject <SOGoSource> *sogoSource;
NSEnumerator *authIDs;
NSString *currentID;
BOOL checkOK;
checkOK = NO;
authIDs = [[self authenticationSourceIDsInDomain: nil] objectEnumerator];
while (!checkOK && (currentID = [authIDs nextObject]))
{
ldapSource = [_sources objectForKey: currentID];
checkOK = [ldapSource checkLogin: login andPassword: password];
sogoSource = [_sources objectForKey: currentID];
checkOK = [sogoSource checkLogin: login
password: password
perr: perr
expire: expire
grace: grace];
}
return checkOK;
}
- (BOOL) checkLogin: (NSString *) login
andPassword: (NSString *) password
- (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace
{
NSMutableDictionary *currentUser;
NSString *dictPassword, *jsonUser;
NSMutableDictionary *currentUser;
BOOL checkOK;
jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: login];
jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: _login];
currentUser = [NSMutableDictionary dictionaryWithJSONString: jsonUser];
dictPassword = [currentUser objectForKey: @"password"];
if (currentUser && dictPassword)
checkOK = ([dictPassword isEqualToString: password]);
else if ([self _sourceCheckLogin: login andPassword: password])
checkOK = ([dictPassword isEqualToString: _pwd]);
else if ([self _sourceCheckLogin: _login
andPassword: _pwd
perr: _perr
expire: _expire
grace: _grace])
{
checkOK = YES;
if (!currentUser)
@ -386,17 +426,58 @@
// set the password and recache the entry, the password would never be
// cached for the user unless its entry expires from memcached's
// internal cache.
[currentUser setObject: password forKey: @"password"];
[currentUser setObject: _pwd forKey: @"password"];
[[SOGoCache sharedCache]
setUserAttributes: [currentUser jsonStringValue]
forLogin: login];
forLogin: _login];
}
else
checkOK = NO;
else
checkOK = NO;
return checkOK;
}
- (BOOL) changePasswordForLogin: (NSString *) login
oldPassword: (NSString *) oldPassword
newPassword: (NSString *) newPassword
perr: (SOGoPasswordPolicyError *) perr
{
NSString *dictPassword, *jsonUser;
NSMutableDictionary *currentUser;
BOOL didChange;
jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: login];
currentUser = [NSMutableDictionary dictionaryWithJSONString: jsonUser];
dictPassword = [currentUser objectForKey: @"password"];
if ([self _sourceChangePasswordForLogin: login
oldPassword: oldPassword
newPassword: newPassword
perr: perr])
{
didChange = YES;
if (!currentUser)
{
currentUser = [NSMutableDictionary dictionary];
}
// It's important to cache the password here as we might have cached the
// user's entry in -contactInfosForUserWithUIDorEmail: and if we don't
// set the password and recache the entry, the password would never be
// cached for the user unless its entry expires from memcached's
// internal cache.
[currentUser setObject: newPassword forKey: @"password"];
[[SOGoCache sharedCache]
setUserAttributes: [currentUser jsonStringValue]
forLogin: login];
}
else
didChange = NO;
return didChange;
}
- (void) _fillContactMailRecords: (NSMutableDictionary *) contact
{
NSString *uid, *domain, *systemEmail;
@ -424,8 +505,8 @@
{
NSMutableArray *emails;
NSDictionary *userEntry;
NSEnumerator *ldapSources;
LDAPSource *currentSource;
NSEnumerator *sogoSources;
NSObject <SOGoDNSource> *currentSource;
NSString *sourceID, *cn, *c_domain, *c_uid, *c_imaphostname;
NSArray *c_emails;
BOOL access;
@ -441,9 +522,9 @@
[currentUser setObject: [NSNumber numberWithBool: YES]
forKey: @"MailAccess"];
ldapSources = [[self authenticationSourceIDsInDomain: nil]
sogoSources = [[self authenticationSourceIDsInDomain: nil]
objectEnumerator];
while ((sourceID = [ldapSources nextObject]))
while ((sourceID = [sogoSources nextObject]))
{
currentSource = [_sources objectForKey: sourceID];
userEntry = [currentSource lookupContactEntryWithUIDorEmail: uid];

View File

@ -1,8 +1,9 @@
/* SOGoWebAuthenticator.h - this file is part of SOGo
*
* Copyright (C) 2007 Inverse inc.
* Copyright (C) 2007-2010 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* 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
@ -24,7 +25,9 @@
#define _SOGOWEBAUTHENTICATOR_H_
#import <NGObjWeb/SoCookieAuthenticator.h>
#import "SOGoAuthenticator.h"
#import "SOGoConstants.h"
@class NSString;
@ -34,6 +37,12 @@
+ (id) sharedSOGoWebAuthenticator;
- (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace;
@end
#endif /* _SOGOWEBAUTHENTICATOR_H__ */
#endif /* _SOGOWEBAUTHENTICATOR_H_ */

View File

@ -1,6 +1,6 @@
/* SOGoWebAuthenticator.m - this file is part of SOGo
*
* Copyright (C) 2007-2009 Inverse inc.
* Copyright (C) 2007-2010 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
@ -37,6 +37,7 @@
#import <MainUI/SOGoRootPage.h>
#import "SOGoCASSession.h"
#import "SOGoConstants.h"
#import "SOGoPermissions.h"
#import "SOGoSystemDefaults.h"
#import "SOGoUser.h"
@ -59,11 +60,28 @@
- (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd
{
SOGoPasswordPolicyError perr;
int expire, grace;
return [self checkLogin: _login
password: _pwd
perr: &perr
expire: &expire
grace: &grace];
}
- (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace
{
SOGoCASSession *session;
SOGoSystemDefaults *sd;
BOOL rc;
SOGoCASSession *session;
sd = [SOGoSystemDefaults sharedSystemDefaults];
if ([[sd authenticationType] isEqualToString: @"cas"])
{
session = [SOGoCASSession CASSessionWithIdentifier: _pwd];
@ -74,8 +92,15 @@
}
else
rc = [[SOGoUserManager sharedUserManager] checkLogin: _login
andPassword: _pwd];
password: _pwd
perr: _perr
expire: _expire
grace: _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;
}

View File

@ -1,6 +1,6 @@
/* SQLSource.h - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
* Copyright (C) 2009-2010 Inverse inc.
*
* Author: Ludovic Marcotte <lmarcotte@inverse.ca>
*
@ -40,6 +40,8 @@
#include "SQLSource.h"
#include "SOGoConstants.h"
/**
* The view MUST contain the following columns:
*
@ -162,8 +164,17 @@
return NO;
}
- (BOOL) checkLogin: (NSString *) login
andPassword: (NSString *) password
//
// SQL sources don't support right now all the password policy
// stuff supported by OpenLDAP (and others). If we want to support
// this for SQL sources, we'll have to implement the same
// kind of logic in this module.
//
- (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace
{
EOAdaptorChannel *channel;
GCSChannelManager *cm;
@ -180,7 +191,7 @@
sql = [NSString stringWithFormat: (@"SELECT c_password"
@" FROM %@"
@" WHERE c_uid = '%@'"),
[_viewURL gcsTableName], login];
[_viewURL gcsTableName], _login];
ex = [channel evaluateExpressionX: sql];
if (!ex)
@ -193,7 +204,7 @@
row = [channel fetchAttributes: attrs withZone: NULL];
value = [row objectForKey: @"c_password"];
rc = [self _isPassword: password equalTo: value];
rc = [self _isPassword: _pwd equalTo: value];
[channel cancelFetch];
}
else
@ -207,6 +218,14 @@
return rc;
}
- (BOOL) changePasswordForLogin: (NSString *) login
oldPassword: (NSString *) oldPassword
newPassword: (NSString *) newPassword
perr: (SOGoPasswordPolicyError *) perr
{
return NO;
}
- (NSDictionary *) _lookupContactEntry: (NSString *) theID
considerEmail: (BOOL) b
{

View File

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

View File

@ -24,6 +24,7 @@
#import <Foundation/NSException.h>
#import <Foundation/NSTimeZone.h>
#import <Foundation/NSURL.h>
#import <Foundation/NSValue.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/WOApplication.h>
@ -39,13 +40,17 @@
#import <NGExtensions/NSObject+Logs.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSDictionary+BSJSONAdditions.h>
#import <SOGo/SOGoCache.h>
#import <SOGo/SOGoCASSession.h>
#import <SOGo/SOGoDomainDefaults.h>
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserManager.h>
#import <SOGo/SOGoWebAuthenticator.h>
#import <SOGo/SOGoConstants.h>
#import "SOGoRootPage.h"
@interface SOGoRootPage (crashAdditions)
@ -115,17 +120,37 @@
SOGoUserDefaults *ud;
NSString *username, *password, *language;
NSArray *supportedLanguages;
SOGoPasswordPolicyError err;
int expire, grace;
BOOL b;
err = PolicyNoError;
expire = grace = 0;
auth = [[WOApplication application]
authenticatorInContext: context];
authenticatorInContext: context];
request = [context request];
username = [request formValueForKey: @"userName"];
password = [request formValueForKey: @"password"];
language = [request formValueForKey: @"language"];
if ([auth checkLogin: username password: password])
if ((b = [auth checkLogin: username
password: password
perr: &err
expire: &expire
grace: &grace])
&& (err == PolicyNoError || err == PolicyChangeAfterReset))
{
[self logWithFormat: @"successful login for user '%@'", username];
response = [self responseWith204];
// We must warn the user that he has to change his password
if (err == PolicyChangeAfterReset)
{
}
authCookie = [self _cookieWithUsername: username andPassword: password
forAuthenticator: auth];
[response addCookie: authCookie];
@ -141,7 +166,21 @@
}
else
{
[self logWithFormat: @"failed login for user '%@'", username];
[self logWithFormat: @"Login for user '%@' might not have worked - password policy: %d bound: ", username, err, b];
if (err == PolicyNoError)
{
[self logWithFormat: @"failed login for user '%@' due to wrong password", username];
}
else if (err == PolicyAccountLocked)
{
// Account has been locked due to too many failures
}
else if (err == PolicyPasswordExpired)
{
// The password MUST be changed - we need to ask for the old password and the new one here
}
response = [self responseWithStatus: 403];
}
@ -372,4 +411,41 @@
return aString;
}
- (WOResponse *) changePasswordAction
{
NSString *username, *password, *newPassword;
SOGoUserManager *um;
SOGoPasswordPolicyError error;
WOResponse *response;
WORequest *request;
NSDictionary *message, *jsonError;
request = [context request];
message = [NSMutableDictionary dictionaryWithJSONString: [request contentAsString]];
username = [message objectForKey: @"userName"];
password = [message objectForKey: @"password"];
newPassword = [message objectForKey: @"newPassword"];
um = [SOGoUserManager sharedUserManager];
// This will also update the cached password in memcached.
if ([um changePasswordForLogin: username
oldPassword: password
newPassword: newPassword
perr: &error])
response = [self responseWith204];
else
{
jsonError
= [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: error]
forKey: @"LDAPPasswordPolicyError"];
response = [self responseWithStatus: 403
andJSONRepresentation: jsonError];
[response setHeader: @"application/json"
forKey: @"content-type"];
}
return response;
}
@end /* SOGoRootPage */

View File

@ -130,6 +130,11 @@
pageName = "SOGoRootPage";
actionName = "connect";
};
changePassword = {
protectedBy = "<public>";
pageName = "SOGoRootPage";
actionName = "changePassword";
};
GET = { // more or less a hack, see README of dbd
protectedBy = "<public>";
pageName = "SOGoRootPage";

View File

@ -34,9 +34,9 @@
><var:if condition="shouldDisplayAdditionalPreferences">
<li target="additionalView"><span>
<var:string label:value="Additional Parameters"/></span></li>
<!-- </var:if -->
<!-- ><var:if condition="shouldDisplayPasswordChange"> -->
<!-- <li target="passwordView"><span><var:string label:value="Password"/></span></li> -->
</var:if
><var:if condition="shouldDisplayPasswordChange">
<li target="passwordView"><span><var:string label:value="Password"/></span></li>
</var:if>
</ul>
<div id="generalView" class="tab">
@ -278,22 +278,22 @@
</div>
</div>
</var:if
></var:if>
<!-- <var:if condition="shouldDisplayPasswordChange"> -->
<!-- <div id="passwordView" class="tab"> -->
<!-- <label><var:string label:value="New password:" -->
<!-- /><input type="text" class="textField" -->
<!-- const:enabled="disabled" -->
<!-- var:value="newPassword"/></label><br/> -->
<!-- <label><var:string label:value="Confirmation:" -->
<!-- /><input type="text" class="textField" -->
<!-- const:enabled="disabled" -->
<!-- var:value="newPasswordConfirmation"/></label><br/> -->
<!-- <input type="button" class="button" -->
<!-- id="changePasswordBtn" label:value="Change"/> -->
<!-- </div> -->
<!-- </var:if> -->
<var:if condition="shouldDisplayAdditionalPreferences"
></var:if
><var:if condition="shouldDisplayPasswordChange">
<div id="passwordView" class="tab">
<p id="passwordFields"><label><var:string label:value="New password:"
/><input const:id="newPasswordField" class="textField"
type="text" const:value=""/></label><br/>
<label><var:string label:value="Confirmation:"
/><input const:id="newPasswordConfirmationField" class="textField"
type="text" const:value=""/></label><br/>
<a href="#" class="button" id="changePasswordBtn"
><span><var:string label:value="Change"/></span></a>
</p>
<p id="passwordError"><!-- space --></p>
</div>
</var:if
><var:if condition="shouldDisplayAdditionalPreferences"
><div id="additionalView" class="tab">
<var:component className="UIxAdditionalPreferences"/>
</div></var:if>

View File

@ -272,8 +272,7 @@ String.prototype.base64decode = function() {
var enc1, enc2, enc3, enc4;
var i = 0;
var input = this.replace(/[^A-Za-z0-9\+\/\=]/g, "");
var input = "" + this; // .replace(/[^A-Za-z0-9\+\/\=]/g, "")
while (i < input.length) {
enc1 = this._base64_keyStr.indexOf(input.charAt(i++));
enc2 = this._base64_keyStr.indexOf(input.charAt(i++));
@ -294,7 +293,7 @@ String.prototype.base64decode = function() {
}
}
return output.utf8decode();
return output;
};
String.prototype.utf8encode = function() {

View File

@ -5,10 +5,6 @@ DIV#preferencesTabs
right: 5px;
bottom: 5px; }
DIV#passwordView
{ padding-top: 3em;
padding-right: 10em; }
TEXTAREA#signature
{ position: absolute;
width: 100%;
@ -107,3 +103,25 @@ TH#activeTableHeader
TD.activeColumn
{ text-align: center; }
P#passwordFields,
P#passwordError
{ position: absolute;
left: 0px; }
#passwordFields
{ top: 40px;
width: 410px;
text-align: right; }
P#passwordError
{ top: 92px;
width: 307px;
text-align: right;
margin-left: 40px; }
P.errorMessage#passwordError
{ color: #f00; }
P.infoMessage#passwordError
{ color: #00f; }

View File

@ -105,7 +105,7 @@ function onChoiceChanged(event) {
_setupEvents(false);
}
function addDefaultEmailAddresses() {
function addDefaultEmailAddresses(event) {
var defaultAddresses = $("defaultEmailAddresses").value.split(/, */);
var addresses = $("autoReplyEmailAddresses").value.trim();
@ -121,6 +121,8 @@ function addDefaultEmailAddresses() {
});
$("autoReplyEmailAddresses").value = addresses.join(", ");
event.stop();
}
function initPreferences() {
@ -157,9 +159,13 @@ function initPreferences() {
onReplyPlacementListChange ();
}
if ($("addDefaultEmailAddresses"))
$("addDefaultEmailAddresses").observe("click",
addDefaultEmailAddresses);
var button = $("addDefaultEmailAddresses");
if (button)
button.observe("click", addDefaultEmailAddresses);
var button = $("changePasswordBtn");
if (button)
button.observe("click", onChangePasswordClick);
initSieveFilters();
}
@ -549,4 +555,126 @@ function onComposeMessagesTypeChange(event) {
}
}
function onChangePasswordClick(event) {
var field = $("newPasswordField");
var confirmationField = $("newPasswordConfirmationField");
if (field && confirmationField) {
var password = field.value;
if (password == confirmationField.value) {
if (password.length > 0)
changePassword(password);
else
showPasswordMessage(_("Password must not be empty."),
"error");
}
else {
showPasswordMessage(_("The passwords do not match."
+ " Please try again."),
"error");
field.focus();
field.select();
}
}
event.stop();
}
/* TODO: this method could serve as a basis for a basic text container (for
example the log console. */
function showPasswordMessage(message, msgType) {
var para = $("passwordError");
if (para) {
if (!msgType)
msgType = "error";
var typeClass = msgType + "Message";
if (!para.typeClass || para.typeClass != typeClass) {
if (para.typeClass) {
para.removeClassName(para.typeClass);
}
para.typeClass = typeClass;
para.addClassName(typeClass);
}
if (!para.message || para.message != message) {
while (para.lastChild) {
para.removeChild(para.lastChild);
}
if (message) {
var sentences = message.split("\n");
para.appendChild(document.createTextNode(sentences[0]));
for (var i = 1; i < sentences.length; i++) {
para.appendChild(document.createElement("br"));
para.appendChild(document.createTextNode(sentences[i]));
}
para.message = message;
}
}
}
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function changePassword(newPassword) {
var loginValues = readLoginCookie();
if (loginValues) {
var content = Object.toJSON({ userName: loginValues[0],
password: loginValues[1],
newPassword: newPassword });
var url = ApplicationBaseURL + "../changePassword";
triggerAjaxRequest(url, changePasswordCallback, loginValues[1], content,
{"content-type": "application/json"} );
}
}
function changePasswordCallback(http) {
if (http.readyState == 4) {
if (isHttpStatus204(http.status)) {
log("it worked");
showPasswordMessage(_("The password was changed successfully."), "info");
setLoginCookie(UserLogin, http.callbackData);
} else {
var error;
log("header: " + http.header);
switch(http.status) {
case 403:
if (http.getResponseHeader("content-type") == "application/json") {
var jsonResponse = http.responseText.evalJSON(false);
var perr = jsonResponse["LDAPPasswordPolicyError"];
// Normal password change failed
if (perr == 65535) {
error = _("Password change failed");
} else if (perr == 3) {
error = _("Password change failed - Permission denied");
} else if (perr == 5) {
error = _("Password change failed - Insufficient password quality");
} else if (perr == 6) {
error = _("Password change failed - Password is too short");
} else if (perr == 7) {
error = _("Password change failed - Password is too young");
} else if (perr == 8) {
error = _("Password change failed - Password is in history");
}
} else {
error = _("Unhandled error code: ") + http.status;
}
break;
case 404:
error = _("Password changing is not supported.");
break;
default:
error = _("Unhandled error code: ") + http.status;
}
showPasswordMessage(error);
}
}
}
document.observe("dom:loaded", initPreferences);

View File

@ -1695,6 +1695,34 @@ AIM = {
i.onComplete(d.body.innerHTML);
}
};
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function readLoginCookie() {
var loginValues = null;
var cookie = readCookie("0xHIGHFLYxSOGo");
if (cookie && cookie.length > 8) {
var value = decodeURIComponent(cookie.substr(8));
loginValues = value.base64decode().split(":");
}
return loginValues;
}
function setLoginCookie(username, password) {
var value = (username + ":" + password).base64encode();
var cookieValue = encodeURIComponent("basic " + value);
window.alert("0xHIGHFLYxSOGo=" + cookieValue);
}
document.observe("dom:loaded", onLoadHandler);