fix(web): restore support of ppolicy OpenLDAP overlay
parent
da366083e9
commit
0c1f9fdb02
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -156,6 +156,59 @@
|
|||
</div>
|
||||
</var:if>
|
||||
|
||||
<!-- Password policy: Password is expired -->
|
||||
<div layout="column" layout-align="center center"
|
||||
ng-switch-when="passwordexpired">
|
||||
<md-icon class="md-accent md-hue-1 sg-icon--large">watch_later</md-icon>
|
||||
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding">
|
||||
<var:string label:value="Your password has expired, please enter a new one below"/>
|
||||
</div>
|
||||
<div flex="100">
|
||||
<div layout="row" layout-xs="column">
|
||||
<md-input-container class="md-block" flex="flex">
|
||||
<label><var:string label:value="Current password"/>
|
||||
</label>
|
||||
<input type="password" sg-no-dirty-check="true" ng-model="app.passwords.oldPassword"/>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block" flex="flex">
|
||||
<label><var:string label:value="New password"/>
|
||||
</label>
|
||||
<input type="password" sg-no-dirty-check="true" ng-model="app.passwords.newPassword"/>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block" flex="flex">
|
||||
<label><var:string label:value="Confirmation"/>
|
||||
</label>
|
||||
<input type="password" name="newPasswordConfirmation" sg-no-dirty-check="true" ng-model="app.passwords.newPasswordConfirmation"/>
|
||||
<div ng-messages="loginForm.newPasswordConfirmation.$error">
|
||||
<div ng-message="newPasswordMismatch"><var:string label:value="Passwords don't match"/></div>
|
||||
</div>
|
||||
</md-input-container>
|
||||
</div>
|
||||
<div layout="row" layout-align="end center">
|
||||
<md-button ng-click="app.changePassword()" type="button" ng-disabled="!app.canChangePassword(loginForm)">
|
||||
<var:string label:value="Change"/>
|
||||
</md-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Password policy: Grace period -->
|
||||
<div layout="column" layout-align="center center"
|
||||
ng-switch-when="passwordwillexpire">
|
||||
<md-icon class="md-accent md-hue-1 sg-icon--large">warning</md-icon>
|
||||
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding" ng-if="app.cn">
|
||||
<var:string label:value="Welcome"/> {{app.cn}}
|
||||
</div>
|
||||
<div class="md-default-theme md-warn md-hue-1 md-bg md-padding">
|
||||
{{app.errorMessage}}
|
||||
<md-button class="md-raised"
|
||||
ng-click="app.loginState = 'passwordexpired'"><var:string label:value="Change your Password"/></md-button>
|
||||
</div>
|
||||
<md-button
|
||||
ng-click="app.continueLogin()"
|
||||
sg-ripple-click="loginContent"><var:string label:value="Continue"/></md-button>
|
||||
</div>
|
||||
|
||||
<!-- Logged in -->
|
||||
<div layout="column" layout-align="center center"
|
||||
ng-switch-when="logged">
|
||||
|
@ -165,6 +218,17 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div layout="column" layout-align="center center"
|
||||
ng-switch-when="message">
|
||||
<md-icon class="md-accent md-hue-1 sg-icon--large">done</md-icon>
|
||||
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding">
|
||||
{{app.errorMessage}}
|
||||
</div>
|
||||
<md-button
|
||||
ng-click="app.continueLogin()"
|
||||
sg-ripple-click="loginContent"><var:string label:value="Continue"/></md-button>
|
||||
</div>
|
||||
|
||||
<!-- Error -->
|
||||
<div layout="column" layout-align="center center"
|
||||
ng-switch-when="error">
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
|
|
Loading…
Reference in New Issue