diff --git a/UI/MainUI/SOGoRootPage.m b/UI/MainUI/SOGoRootPage.m
index 04cf81551..9caa6293c 100644
--- a/UI/MainUI/SOGoRootPage.m
+++ b/UI/MainUI/SOGoRootPage.m
@@ -166,9 +166,8 @@
{
NSDictionary *jsonError;
- jsonError
- = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: error]
- forKey: @"LDAPPasswordPolicyError"];
+ jsonError = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: error]
+ forKey: @"LDAPPasswordPolicyError"];
return [self responseWithStatus: 403
andJSONRepresentation: jsonError];
}
@@ -649,57 +648,90 @@
request = [context request];
message = [[request contentAsString] objectFromJSONString];
- auth = [[WOApplication application]
- authenticatorInContext: context];
- value = [[context request]
- cookieValueForKey: [auth cookieNameInContext: context]];
- creds = [auth parseCredentials: value];
+ auth = [[WOApplication application] authenticatorInContext: context];
+ value = [[context request] cookieValueForKey: [auth cookieNameInContext: context]];
+ creds = nil;
+ username = nil;
- [SOGoSession decodeValue: [SOGoSession valueForSessionKey: [creds objectAtIndex: 1]]
- usingKey: [creds objectAtIndex: 0]
- login: &username
- domain: &domain
- password: &password];
+ if (value)
+ {
+ // User is logged in; extract username from session
+ creds = [auth parseCredentials: value];
+
+ [SOGoSession decodeValue: [SOGoSession valueForSessionKey: [creds objectAtIndex: 1]]
+ usingKey: [creds objectAtIndex: 0]
+ login: &username
+ domain: &domain
+ password: &password];
+ }
+ else
+ {
+ // We are using ppolicy and changing the password upon login
+ username = [message objectForKey: @"userName"];
+ domain = [message objectForKey: @"domain"];
+ }
newPassword = [message objectForKey: @"newPassword"];
// overwrite the value from the session to compare the actual input
password = [message objectForKey: @"oldPassword"];
- um = [SOGoUserManager sharedUserManager];
-
- // This will also update the cached password in memcached.
- if ([um changePasswordForLogin: username
- inDomain: domain
- oldPassword: password
- newPassword: newPassword
- perr: &error])
+ // Validate required parameters
+ if (!username)
{
- // We delete the previous session
- [SOGoSession deleteValueForSessionKey: [creds objectAtIndex: 1]];
-
- if ([domain isNotNull])
- {
- sd = [SOGoSystemDefaults sharedSystemDefaults];
- if ([sd enableDomainBasedUID] &&
- [username rangeOfString: @"@"].location == NSNotFound)
- username = [NSString stringWithFormat: @"%@@%@", username, domain];
- }
-
- response = [self responseWith204];
- authCookie = [auth cookieWithUsername: username
- andPassword: newPassword
- inContext: context];
- [response addCookie: authCookie];
-
- // We update the XSRF protection cookie
- creds = [auth parseCredentials: [authCookie value]];
- xsrfCookie = [WOCookie cookieWithName: @"XSRF-TOKEN"
- value: [[SOGoSession valueForSessionKey: [creds lastObject]] asSHA1String]];
- [xsrfCookie setPath: [NSString stringWithFormat: @"/%@/", [request applicationName]]];
- [response addCookie: xsrfCookie];
+ response = [self responseWithStatus: 403
+ andString: @"Missing 'username' parameter"];
+ }
+ else if (!password)
+ {
+ response = [self responseWithStatus: 403
+ andString: @"Missing 'oldPassword' parameter"];
+ }
+ else if (!newPassword)
+ {
+ response = [self responseWithStatus: 403
+ andString: @"Missing 'newPassword' parameter"];
}
else
- response = [self _responseWithLDAPPolicyError: error];
+ {
+ um = [SOGoUserManager sharedUserManager];
+
+ // This will also update the cached password in memcached.
+ if ([um changePasswordForLogin: username
+ inDomain: domain
+ oldPassword: password
+ newPassword: newPassword
+ perr: &error])
+ {
+ if (creds)
+ {
+ // We delete the previous session
+ [SOGoSession deleteValueForSessionKey: [creds objectAtIndex: 1]];
+ }
+
+ if ([domain isNotNull])
+ {
+ sd = [SOGoSystemDefaults sharedSystemDefaults];
+ if ([sd enableDomainBasedUID] &&
+ [username rangeOfString: @"@"].location == NSNotFound)
+ username = [NSString stringWithFormat: @"%@@%@", username, domain];
+ }
+
+ response = [self responseWith204];
+ authCookie = [auth cookieWithUsername: username
+ andPassword: newPassword
+ inContext: context];
+ [response addCookie: authCookie];
+
+ // We update the XSRF protection cookie
+ creds = [auth parseCredentials: [authCookie value]];
+ xsrfCookie = [WOCookie cookieWithName: @"XSRF-TOKEN"
+ value: [[SOGoSession valueForSessionKey: [creds lastObject]] asSHA1String]];
+ [xsrfCookie setPath: [NSString stringWithFormat: @"/%@/", [request applicationName]]];
+ [response addCookie: xsrfCookie];
+ }
+ else
+ response = [self _responseWithLDAPPolicyError: error];
+ }
return response;
}
diff --git a/UI/Templates/MainUI/SOGoRootPage.wox b/UI/Templates/MainUI/SOGoRootPage.wox
index 7912d1d62..0c6f7f617 100644
--- a/UI/Templates/MainUI/SOGoRootPage.wox
+++ b/UI/Templates/MainUI/SOGoRootPage.wox
@@ -156,6 +156,59 @@
+
+
+
+
+
+
warning
+
+ {{app.cn}}
+
+
+ {{app.errorMessage}}
+
+
+
+
+
@@ -165,6 +218,17 @@
+
+
done
+
+ {{app.errorMessage}}
+
+
+
+
diff --git a/UI/WebServerResources/js/Common/Authentication.service.js b/UI/WebServerResources/js/Common/Authentication.service.js
index 9d2fd4e6b..c801fdcb1 100644
--- a/UI/WebServerResources/js/Common/Authentication.service.js
+++ b/UI/WebServerResources/js/Common/Authentication.service.js
@@ -100,46 +100,68 @@
// Check password policy
else if (typeof data.expire != 'undefined' && typeof data.grace != 'undefined') {
if (data.expire < 0 && data.grace > 0) {
- d.reject({grace: data.grace});
- //showPasswordDialog('grace', createPasswordGraceDialog, data['grace']);
+ d.reject({
+ cn: data.cn,
+ url: redirectUrl(username, domain),
+ grace: data.grace
+ });
} else if (data.expire > 0 && data.grace == -1) {
- d.reject({expire: data.expire});
- //showPasswordDialog('expiration', createPasswordExpirationDialog, data['expire']);
+ d.reject({
+ cn: data.cn,
+ url: redirectUrl(username, domain),
+ expire: data.expire
+ });
}
else {
- d.resolve({ cn: data.cn, url: redirectUrl(username, domain) });
+ d.resolve({
+ cn: data.cn,
+ url: redirectUrl(username, domain)
+ });
}
}
else {
d.resolve({ url: redirectUrl(username, domain) });
}
}
- }, function(response) {
- var msg, perr, data = response.data;
+ }, function(error) {
+ var response, perr, data = error.data;
if (data && data.GoogleAuthenticatorInvalidKey) {
- msg = l('You provided an invalid Google Authenticator key.');
+ response = {error: l('You provided an invalid Google Authenticator key.')};
}
- else if (data && data.LDAPPasswordPolicyError) {
+ else if (data && angular.isDefined(data.LDAPPasswordPolicyError)) {
perr = data.LDAPPasswordPolicyError;
if (perr == passwordPolicyConfig.PolicyNoError) {
- msg = l('Wrong username or password.');
+ response = {error: l('Wrong username or password.')};
}
else if (perr == passwordPolicyConfig.PolicyAccountLocked) {
- msg = l('Your account was locked due to too many failed attempts.');
+ response = {error: l('Your account was locked due to too many failed attempts.')};
+ }
+ else if (perr == passwordPolicyConfig.PolicyPasswordExpired ||
+ perr == passwordPolicyConfig.PolicyChangeAfterReset) {
+ response = {
+ passwordexpired: 1,
+ url: redirectUrl(username, domain)
+ };
+ }
+ else if (perr == passwordPolicyConfig.PolicyChangeAfterReset) {
+ response = {
+ passwordexpired: 1,
+ url: redirectUrl(username, domain)
+ };
}
else {
- msg = l('Login failed due to unhandled error case: ') + perr;
+ response = {error: l('Login failed due to unhandled error case: ') + perr};
}
}
else {
- msg = l('Unhandled error response');
+ response = {error: l('Unhandled error response')};
}
- d.reject({error: msg});
+ d.reject(response);
});
return d.promise;
}, // login: function(data) { ...
- changePassword: function(newPassword, oldPassword) {
+ changePassword: function(userName, domain, newPassword, oldPassword) {
var d = $q.defer(),
xsrfCookie = $cookies.get('XSRF-TOKEN');
@@ -151,8 +173,10 @@
headers: {
'X-XSRF-TOKEN' : xsrfCookie
},
- data: { newPassword: newPassword, oldPassword: oldPassword }
- }).then(d.resolve, function(response) {
+ data: { userName: userName, newPassword: newPassword, oldPassword: oldPassword }
+ }).then(function() {
+ d.resolve({url: redirectUrl(userName, domain)});
+ }, function(response) {
var error,
data = response.data,
perr = data.LDAPPasswordPolicyError;
@@ -161,7 +185,8 @@
perr = passwordPolicyConfig.PolicyPasswordSystemUnknown;
error = _("Unhandled error response");
}
- else if (perr == passwordPolicyConfig.PolicyNoError) {
+ else if (perr == passwordPolicyConfig.PolicyNoError ||
+ perr == passwordPolicyConfig.PolicyPasswordUnknown) {
error = l("Password change failed");
} else if (perr == passwordPolicyConfig.PolicyPasswordModNotAllowed) {
error = l("Password change failed - Permission denied");
diff --git a/UI/WebServerResources/js/Main/Main.app.js b/UI/WebServerResources/js/Main/Main.app.js
index a3e37a969..7996420b1 100644
--- a/UI/WebServerResources/js/Main/Main.app.js
+++ b/UI/WebServerResources/js/Main/Main.app.js
@@ -17,6 +17,7 @@
this.creds = {
username: $window.cookieUsername,
password: null,
+ domain: null,
rememberLogin: angular.isDefined($window.cookieUsername) && $window.cookieUsername.length > 0
};
// Send selected language only if user has changed it
@@ -27,6 +28,9 @@
// Code pattern for Google verification code
this.verificationCodePattern = '\\d{6}';
+ // Password policy - change expired password
+ this.passwords = { newPassword: null, newPasswordConfirmation: null, oldPassword: null };
+
// Show login once everything is initialized
this.showLogin = false;
$timeout(function() { vm.showLogin = true; }, 100);
@@ -43,18 +47,55 @@
else {
vm.loginState = 'logged';
vm.cn = data.cn;
+ vm.url = data.url;
// Let the user see the succesfull message before reloading the page
$timeout(function() {
- if ($window.location.href === data.url)
- $window.location.reload(true);
- else
- $window.location.href = data.url;
+ vm.continueLogin();
}, 1000);
}
}, function(msg) {
vm.loginState = 'error';
- vm.errorMessage = msg.error;
+
+ if (msg.error) {
+ vm.errorMessage = msg.error;
+ }
+ else if (msg.grace > 0) {
+ // Password is expired, grace logins limit is not yet reached
+ vm.loginState = 'passwordwillexpire';
+ vm.cn = msg.cn;
+ vm.url = msg.url;
+ vm.errorMessage = l('You have %{0} logins remaining before your account is locked. Please change your password in the preference dialog.', msg.grace);
+ }
+ else if (msg.expire > 0) {
+ // Password will soon expire
+ var value, string;
+ if (msg.expire > 86400) {
+ value = Math.round(msg.expire/86400);
+ string = l("days");
+ }
+ else if (msg.expire > 3600) {
+ value = Math.round(msg.expire/3600);
+ string = l("hours");
+ }
+ else if (msg.expire > 60) {
+ value = Math.round(msg.expire/60);
+ string = l("minutes");
+ }
+ else {
+ value = msg.expire;
+ string = l("seconds");
+ }
+ vm.loginState = 'passwordwillexpire';
+ vm.cn = msg.cn;
+ vm.url = msg.url;
+ vm.errorMessage = l('Your password is going to expire in %{0} %{1}.', value, string);
+ }
+ else if (msg.passwordexpired) {
+ vm.loginState = 'passwordexpired';
+ vm.url = msg.url;
+ }
+
});
return false;
};
@@ -64,6 +105,13 @@
delete vm.creds.verificationCode;
};
+ this.continueLogin = function() {
+ if ($window.location.href === vm.url)
+ $window.location.reload(true);
+ else
+ $window.location.href = vm.url;
+ };
+
this.showAbout = function($event) {
$mdDialog.show({
targetEvent: $event,
@@ -83,6 +131,36 @@
// Reload page
$window.location.href = ApplicationBaseURL + 'login?language=' + this.creds.language;
};
+
+ this.canChangePassword = function(form) {
+ if (this.passwords.newPasswordConfirmation && this.passwords.newPasswordConfirmation.length &&
+ this.passwords.newPassword != this.passwords.newPasswordConfirmation) {
+ form.newPasswordConfirmation.$setValidity('newPasswordMismatch', false);
+ return false;
+ }
+ else {
+ form.newPasswordConfirmation.$setValidity('newPasswordMismatch', true);
+ }
+ if (this.passwords.newPassword && this.passwords.newPassword.length > 0 &&
+ this.passwords.newPasswordConfirmation && this.passwords.newPasswordConfirmation.length &&
+ this.passwords.newPassword == this.passwords.newPasswordConfirmation &&
+ this.passwords.oldPassword && this.passwords.oldPassword.length > 0)
+ return true;
+
+ return false;
+ };
+
+ this.changePassword = function() {
+ Authentication.changePassword(this.creds.username, this.creds.domain, this.passwords.newPassword, this.passwords.oldPassword).then(function(data) {
+ vm.loginState = 'message';
+ vm.url = data.url;
+ vm.errorMessage = l('The password was changed successfully.');
+ }, function(msg) {
+ vm.loginState = 'error';
+ vm.errorMessage = msg;
+ });
+ };
+
}
angular
diff --git a/UI/WebServerResources/js/Preferences/PreferencesController.js b/UI/WebServerResources/js/Preferences/PreferencesController.js
index f563f3e54..73cb7ed09 100644
--- a/UI/WebServerResources/js/Preferences/PreferencesController.js
+++ b/UI/WebServerResources/js/Preferences/PreferencesController.js
@@ -484,7 +484,7 @@
};
this.changePassword = function() {
- Authentication.changePassword(this.passwords.newPassword, this.passwords.oldPassword).then(function() {
+ Authentication.changePassword(null, null, this.passwords.newPassword, this.passwords.oldPassword).then(function() {
var alert = $mdDialog.alert({
title: l('Password'),
textContent: l('The password was changed successfully.'),
@@ -497,7 +497,7 @@
}, function(msg) {
var alert = $mdDialog.alert({
title: l('Password'),
- content: msg,
+ textContent: msg,
ok: l('OK')
});
$mdDialog.show( alert )