feat(core): Check password strength on login (SQL Source). Closes #6025.

This commit is contained in:
smizrahi
2024-11-27 16:39:53 +01:00
parent 2f9c2cf4da
commit fcaaebcc66
13 changed files with 269 additions and 132 deletions

View File

@@ -2306,7 +2306,7 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection,
- (NSArray *) membersForGroupWithUID: (NSString *) uid
{
NSMutableArray *dns, *uids, *userLogins;
NSMutableArray *dns, *uids;
NSString *dn, *login;
SOGoUserManager *um;
NSDictionary *d, *contactInfos;
@@ -2327,7 +2327,6 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection,
members = [NSMutableArray new];
uids = [NSMutableArray array];
dns = [NSMutableArray array];
userLogins = [NSMutableArray array];
// We check if it's a static group
// Fetch "members" - we get DNs
@@ -2359,13 +2358,6 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection,
pool = [NSAutoreleasePool new];
dn = [dns objectAtIndex: i];
login = [um getLoginForDN: [dn lowercaseString]];
if([userLogins containsObject: login])
{
[pool release];
continue; //user alrady fetch
}
if(login != nil)
[userLogins addObject: login];
user = [SOGoUser userWithLogin: login roles: nil];
if (user)
{
@@ -2393,13 +2385,6 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection,
{
pool = [NSAutoreleasePool new];
login = [uids objectAtIndex: i];
if([userLogins containsObject: login])
{
[pool release];
continue; //user alrady fetch
}
if(login != nil)
[userLogins addObject: login];
user = [SOGoUser userWithLogin: login roles: nil];
if (user)
{

View File

@@ -65,7 +65,8 @@
domain: &domain
perr: &perr
expire: &expire
grace: &grace]
grace: &grace
additionalInfo: nil]
&& perr == PolicyNoError);
if (!rc)

View File

@@ -502,6 +502,7 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
perr: (SOGoPasswordPolicyError *) perr
expire: (int *) expire
grace: (int *) grace
additionalInfo: (NSMutableDictionary **)_additionalInfo
{
NSObject <SOGoSource> *sogoSource;
NSEnumerator *authIDs;
@@ -520,8 +521,11 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
perr: perr
expire: expire
grace: grace];
if ([sogoSource userPasswordPolicy] && [[sogoSource userPasswordPolicy] count] > 0) {
[*_additionalInfo setObject:[sogoSource userPasswordPolicy] forKey:@"userPolicies"];
}
}
if (checkOK && *domain == nil)
{
SOGoSystemDefaults *sd = [SOGoSystemDefaults sharedSystemDefaults];
@@ -554,6 +558,7 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace
additionalInfo:(NSMutableDictionary **)_additionalInfo
{
return [self checkLogin: _login
password: _pwd
@@ -561,6 +566,7 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
perr: _perr
expire: _expire
grace: _grace
additionalInfo: _additionalInfo
useCache: YES];
}
@@ -573,6 +579,7 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace
additionalInfo: (NSMutableDictionary **)_additionalInfo
useCache: (BOOL) useCache
{
NSString *dictPassword, *username, *jsonUser;
@@ -726,7 +733,8 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
domain: _domain
perr: _perr
expire: _expire
grace: _grace])
grace: _grace
additionalInfo: _additionalInfo])
{
checkOK = YES;
if (!currentUser)

View File

@@ -37,20 +37,22 @@
+ (id) sharedSOGoWebAuthenticator;
- (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd
domain: (NSString **) _domain
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace;
- (BOOL)checkLogin:(NSString *)_login
password:(NSString *)_pwd
domain:(NSString **)_domain
perr:(SOGoPasswordPolicyError *)_perr
expire:(int *)_expire
grace:(int *)_grace
additionalInfo:(NSMutableDictionary **)_additionalInfo;
- (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd
domain: (NSString **) _domain
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace
useCache: (BOOL) useCache;
- (BOOL)checkLogin:(NSString *)_login
password:(NSString *)_pwd
domain:(NSString **)_domain
perr:(SOGoPasswordPolicyError *)_perr
expire:(int *)_expire
grace:(int *)_grace
additionalInfo:(NSMutableDictionary **)_additionalInfo
useCache:(BOOL)useCache;
- (WOCookie *) cookieWithUsername: (NSString *) username
andPassword: (NSString *) password

View File

@@ -102,7 +102,8 @@
domain: &domain
perr: &perr
expire: &expire
grace: &grace];
grace: &grace
additionalInfo: nil];
}
- (BOOL) checkLogin: (NSString *) _login
@@ -111,6 +112,7 @@
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace
additionalInfo: (NSMutableDictionary **)_additionalInfo
{
return [self checkLogin: _login
password: _pwd
@@ -118,6 +120,7 @@
perr: _perr
expire: _expire
grace: _grace
additionalInfo: _additionalInfo
useCache: YES];
}
@@ -127,6 +130,7 @@
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace
additionalInfo: (NSMutableDictionary **)_additionalInfo
useCache: (BOOL) _useCache
{
SOGoCASSession *session;
@@ -164,6 +168,7 @@
perr: _perr
expire: _expire
grace: _grace
additionalInfo: _additionalInfo
useCache: _useCache];
//[self logWithFormat: @"Checked login with ppolicy enabled: %d %d %d", *_perr, *_expire, *_grace];
@@ -250,7 +255,8 @@
domain: &domain
perr: &perr
expire: &expire
grace: &grace])
grace: &grace
additionalInfo: nil])
return nil;
if (domain && [login rangeOfString: @"@"].location == NSNotFound)

View File

@@ -245,6 +245,20 @@
return result;
}
- (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace
{
return [self checkLogin: _login
password: _pwd
perr: _perr
expire: _perr
grace: _grace
disablepasswordPolicyCheck: NO];
}
//
// SQL sources don't support right now all the password policy
// stuff supported by OpenLDAP (and others). If we want to support
@@ -256,6 +270,7 @@
perr: (SOGoPasswordPolicyError *) _perr
expire: (int *) _expire
grace: (int *) _grace
disablepasswordPolicyCheck: (BOOL) _disablepasswordPolicyCheck
{
EOAdaptorChannel *channel;
EOQualifier *qualifier;
@@ -335,10 +350,69 @@
else
[self errorWithFormat:@"failed to acquire channel for URL: %@",
[_viewURL absoluteString]];
if (YES == rc && !_disablepasswordPolicyCheck) {
[self checkPasswordPolicyWithPassword:_pwd perr: _perr];
}
return rc;
}
/**
* Validates a given password against the configured user password policies.
*
* This method checks if the provided password complies with all defined
* password policies for the user. Each policy is expected to include
* a regular expression (`regex`) that the password must match.
*
* If the password violates any policy, the method sets an appropriate error
* in the `perr` parameter and stops further validation.
*
* @param password the password to validate.
* @param perr will be set to indicate a policy violation, if the password
* does not meet the required standards. Possible values include:
* `PolicyInsufficientPasswordQuality` for insufficient complexity.
* @return YES if the password satisfies all policies, NO otherwise.
*
* @note This method assumes that `_userPasswordPolicy` is an array of dictionaries,
* where each dictionary represents a password policy with at least a "regex" key.
* @warning If a policy does not include a "regex" key, an error will be logged, and
* the method will continue to the next policy.
*/
- (BOOL) checkPasswordPolicyWithPassword: (NSString *)password perr: (SOGoPasswordPolicyError *)perr
{
BOOL isPolicyOk;
NSDictionary *policy;
NSEnumerator *policies;
NSRange match;
NSString *regex;
isPolicyOk = YES;
if ([_userPasswordPolicy count])
{
policies = [_userPasswordPolicy objectEnumerator];
while (isPolicyOk && (policy = [policies nextObject]))
{
regex = [policy objectForKey: @"regex"];
if (regex)
{
match = [password rangeOfString: regex options: NSRegularExpressionSearch];
isPolicyOk = isPolicyOk && match.length > 0;
if (match.length == 0)
{
// [self errorWithFormat: @"Password not conform to policy %@ (%@)", regex, [policy objectForKey: @"label"]];
*perr = PolicyInsufficientPasswordQuality;
}
}
else
[self errorWithFormat: @"Invalid password policy (missing regex): %@", policy];
}
}
return isPolicyOk;
}
/**
* Change a user's password.
* @param login the user's login name.
@@ -357,11 +431,8 @@
BOOL didChange, isOldPwdOk, isPolicyOk;
EOAdaptorChannel *channel;
GCSChannelManager *cm;
NSDictionary *policy;
NSEnumerator *policies;
NSException *ex;
NSRange match;
NSString *sqlstr, *regex;
NSString *sqlstr;
*perr = -1;
isOldPwdOk = NO;
@@ -369,30 +440,11 @@
didChange = NO;
// Verify current password
isOldPwdOk = [self checkLogin:login password:oldPassword perr:perr expire:0 grace:0];
isOldPwdOk = [self checkLogin:login password:oldPassword perr:perr expire:0 grace:0 disablepasswordPolicyCheck: YES];
if (isOldPwdOk || passwordRecovery)
{
if ([_userPasswordPolicy count])
{
policies = [_userPasswordPolicy objectEnumerator];
while (isPolicyOk && (policy = [policies nextObject]))
{
regex = [policy objectForKey: @"regex"];
if (regex)
{
match = [newPassword rangeOfString: regex options: NSRegularExpressionSearch];
isPolicyOk = isPolicyOk && match.length > 0;
if (match.length == 0)
{
// [self errorWithFormat: @"Password not conform to policy %@ (%@)", regex, [policy objectForKey: @"label"]];
*perr = PolicyInsufficientPasswordQuality;
}
}
else
[self errorWithFormat: @"Invalid password policy (missing regex): %@", policy];
}
}
isPolicyOk = [self checkPasswordPolicyWithPassword:newPassword perr: perr];
}
if ((isOldPwdOk || passwordRecovery) && isPolicyOk)

View File

@@ -88,6 +88,7 @@ See <a href=\"http://www.sogo.nu/en/support/community.html\">this page</a> for v
"Change your Password" = "Change your Password";
"The password was changed successfully." = "The password was changed successfully.";
"Your password has expired, please enter a new one below" = "Your password has expired, please enter a new one below";
"Your password is too weak. Please choose a stronger password to enhance your security" = "Your password is too weak. Please choose a stronger password to enhance your security";
"Password must not be empty." = "Password must not be empty.";
"The passwords do not match. Please try again." = "The passwords do not match. Please try again.";
"Password Grace Period" = "Password Grace Period";
@@ -130,4 +131,11 @@ See <a href=\"http://www.sogo.nu/en/support/community.html\">this page</a> for v
"Invalid configuration for email password recovery" = "Invalid configuration for email password recovery";
"Password recovery email in error" = "Password recovery email in error";
"Password reset" = "Password reset";
"Hi %{0},\nThere was a request to change your password!\n\nIf you did not make this request then please ignore this email.\n\nOtherwise, please click this link to change your password: %{1}" = "Hi %{0},\nThere was a request to change your password!\n\nIf you did not make this request then please ignore this email.\n\nOtherwise, please click this link to change your password: %{1}";
"Hi %{0},\nThere was a request to change your password!\n\nIf you did not make this request then please ignore this email.\n\nOtherwise, please click this link to change your password: %{1}" = "Hi %{0},\nThere was a request to change your password!\n\nIf you did not make this request then please ignore this email.\n\nOtherwise, please click this link to change your password: %{1}";
/* Password */
"POLICY_MIN_LOWERCASE_LETTER" = "Minimum of %{0} lowercase letter";
"POLICY_MIN_UPPERCASE_LETTER" = "Minimum of %{0} uppercase letter";
"POLICY_MIN_DIGIT" = "Minimum of %{0} digit";
"POLICY_MIN_SPECIAL_SYMBOLS" = "Minimum of %{0} special symbols";
"POLICY_MIN_LENGTH" = "Minimum length of %{0} characters";

View File

@@ -85,6 +85,7 @@
"Change your Password" = "Changez votre mot de passe";
"The password was changed successfully." = "Votre mot de passe a bien été changé.";
"Your password has expired, please enter a new one below" = "Votre mot de passe est expiré, veuillez entrer un nouveau mot de passe";
"Your password is too weak. Please choose a stronger password to enhance your security" = "Votre mot de passe est trop faible. Veuillez choisir un mot de passe plus sécurisé pour renforcer votre protection";
"Password must not be empty." = "Le mot de passe ne doit pas être vide.";
"The passwords do not match. Please try again." = "Les mots de passe ne sont pas identiques. Essayez de nouveau.";
"Password Grace Period" = "Période de grâce pour le mot de passe";
@@ -127,4 +128,11 @@
"Invalid configuration for email password recovery" = "Configuration invalide pour la récupération de mot de passe par e-mail";
"Password recovery email in error" = "Erreur lors de l'envoi de l'email de récupération";
"Password reset" = "Réinitialisation de mot de passe";
"Hi %{0},\nThere was a request to change your password!\n\nIf you did not make this request then please ignore this email.\n\nOtherwise, please click this link to change your password: %{1}" = "Bonjour %{0},\nUne demande de changement de mot de passe a été initiée.\n\nSi vous n'êtes pas à l'origine de cet e-mail, n'en tenez pas compte.\n\nSi vous en êtes bien à l'origine, veuillez cliquer sur le lien ci-dessous pour modifier votre mot de passe: %{1}";
"Hi %{0},\nThere was a request to change your password!\n\nIf you did not make this request then please ignore this email.\n\nOtherwise, please click this link to change your password: %{1}" = "Bonjour %{0},\nUne demande de changement de mot de passe a été initiée.\n\nSi vous n'êtes pas à l'origine de cet e-mail, n'en tenez pas compte.\n\nSi vous en êtes bien à l'origine, veuillez cliquer sur le lien ci-dessous pour modifier votre mot de passe: %{1}";
/* Password */
"POLICY_MIN_LOWERCASE_LETTER" = "Au moins %{0} lettre(s) minuscule(s)";
"POLICY_MIN_UPPERCASE_LETTER" = "Au moins %{0} lettre(s) majuscule(s)";
"POLICY_MIN_DIGIT" = "Au moins %{0} chiffre(s)";
"POLICY_MIN_SPECIAL_SYMBOLS" = "Au moins %{0} caractère(s) special(aux)";
"POLICY_MIN_LENGTH" = "Longueur d'au moins %{0} caractère(s)";

View File

@@ -52,6 +52,7 @@
#import <SOGo/SOGoEmptyAuthenticator.h>
#import <SOGo/SOGoMailer.h>
#import <SOGo/SOGoAdmin.h>
#import <SOGo/SOGoPasswordPolicy.h>
#if defined(MFA_CONFIG)
#include <liboath/oath.h>
@@ -167,14 +168,23 @@ static const NSString *kJwtKey = @"jwt";
//
//
//
- (WOResponse *) _responseWithLDAPPolicyError: (int) error
- (WOResponse *) _responseWithLDAPPolicyError: (int) error additionalInfos: (NSDictionary *) additionalInfos
{
NSDictionary *jsonError;
jsonError = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: error]
forKey: @"LDAPPasswordPolicyError"];
return [self responseWithStatus: 403
andJSONRepresentation: jsonError];
if (additionalInfos) {
jsonError = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:error], @"LDAPPasswordPolicyError",
additionalInfos, @"additionalInfos",
nil];
} else {
jsonError = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:error], @"LDAPPasswordPolicyError",
nil];
}
return [self responseWithStatus:403
andJSONRepresentation:jsonError];
}
- (void) _checkAutoReloadWebCalendars: (SOGoUser *) loggedInUser
@@ -197,6 +207,27 @@ static const NSString *kJwtKey = @"jwt";
}
}
//
//
//
- (void)translateAdditionalLoginInformations:(NSMutableDictionary **)additionalLoginInformations
{
NSDictionary *policy;
NSMutableDictionary *translations;
if (additionalLoginInformations && *additionalLoginInformations) {
if ([*additionalLoginInformations objectForKey:@"userPolicies"]) {
translations = [[NSMutableDictionary alloc] init];
for (policy in [*additionalLoginInformations objectForKey:@"userPolicies"]) {
[translations setObject:[self labelForKey: [policy objectForKey:@"label"]] forKey: [policy objectForKey:@"label"]];
}
[*additionalLoginInformations setObject:[SOGoPasswordPolicy createPasswordPolicyLabels: [*additionalLoginInformations objectForKey:@"userPolicies"] withTranslations: translations]
forKey:@"userPolicies"];
[translations release];
}
}
}
//
//
//
@@ -210,6 +241,7 @@ static const NSString *kJwtKey = @"jwt";
SOGoUserSettings *us;
SOGoUser *loggedInUser;
NSDictionary *params;
NSMutableDictionary *additionalLoginInformations;
NSString *username, *password, *language, *domain, *remoteHost;
NSArray *supportedLanguages, *creds;
@@ -223,6 +255,7 @@ static const NSString *kJwtKey = @"jwt";
auth = [[WOApplication application] authenticatorInContext: context];
request = [context request];
params = [[request contentAsString] objectFromJSONString];
additionalLoginInformations = [[NSMutableDictionary alloc] init];
username = [params objectForKey: @"userName"];
password = [params objectForKey: @"password"];
@@ -232,9 +265,11 @@ static const NSString *kJwtKey = @"jwt";
/* this will always be set to something more or less useful by
* [WOHttpTransaction applyAdaptorHeadersWithHttpRequest] */
remoteHost = [request headerForKey:@"x-webobjects-remote-host"];
b = [auth checkLogin: username password: password domain: &domain
perr: &err expire: &expire grace: &grace additionalInfo: &additionalLoginInformations useCache: NO];
[self translateAdditionalLoginInformations: &additionalLoginInformations];
if ((b = [auth checkLogin: username password: password domain: &domain
perr: &err expire: &expire grace: &grace useCache: NO])
if (b
&& (err == PolicyNoError)
// no password policy
&& ((expire < 0 && grace < 0) // no password policy or everything is alright
@@ -334,7 +369,7 @@ static const NSString *kJwtKey = @"jwt";
#endif
if ([us objectForKey: @"ForceResetPassword"]) {
response = [self _responseWithLDAPPolicyError: PolicyPasswordExpired];
response = [self _responseWithLDAPPolicyError: PolicyPasswordExpired additionalInfos: additionalLoginInformations];
} else {
[self _checkAutoReloadWebCalendars: loggedInUser];
@@ -377,7 +412,7 @@ static const NSString *kJwtKey = @"jwt";
[self logWithFormat: @"Login from '%@' for user '%@' might not have worked - password policy: %d grace: %d expire: %d bound: %d",
remoteHost, username, err, grace, expire, b];
response = [self _responseWithLDAPPolicyError: err];
response = [self _responseWithLDAPPolicyError: err additionalInfos: additionalLoginInformations];
}
if (rememberLogin)
@@ -385,6 +420,8 @@ static const NSString *kJwtKey = @"jwt";
else
[response addCookie: [self _cookieWithUsername: nil]];
[additionalLoginInformations release];
return response;
}
@@ -808,7 +845,7 @@ static const NSString *kJwtKey = @"jwt";
}
}
else
response = [self _responseWithLDAPPolicyError: error];
response = [self _responseWithLDAPPolicyError: error additionalInfos: nil];
}
return response;
@@ -1072,4 +1109,5 @@ static const NSString *kJwtKey = @"jwt";
urlCreateAccount];
}
@end /* SOGoRootPage */

View File

@@ -213,17 +213,18 @@
<!-- Password policy: Password is expired / password recovery-->
<div layout="column" layout-align="center center"
ng-switch-when="passwordchange">
<md-icon class="md-accent md-hue-1 sg-icon--large" ng-if="!app.isInPasswordRecoveryMode()">watch_later</md-icon>
<md-icon class="md-accent md-hue-1 sg-icon--large" ng-if="app.isInPasswordRecoveryMode()">vpn_key</md-icon>
<md-icon class="md-accent md-hue-1 sg-icon--large" ng-if="!(app.isInPasswordRecoveryMode() || app.isPasswordExpiredSecurity())">watch_later</md-icon>
<md-icon class="md-accent md-hue-1 sg-icon--large" ng-if="app.isInPasswordRecoveryMode() || app.isPasswordExpiredSecurity()">vpn_key</md-icon>
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding" ng-if="!app.isInPasswordRecoveryMode()">
<var:string label:value="Your password has expired, please enter a new one below"/>
<span ng-if="!app.isPasswordExpiredSecurity()"><var:string label:value="Your password has expired, please enter a new one below"/></span>
<span ng-if="app.isPasswordExpiredSecurity()"><var:string label:value="Your password is too weak. Please choose a stronger password to enhance your security"/></span>
</div>
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding" ng-if="app.isInPasswordRecoveryMode()">
<var:string label:value="Please enter a new password below"/>
</div>
<div flex="100">
<div layout="row" layout-xs="column">
<md-input-container class="md-block" flex="flex" ng-if="!app.isInPasswordRecoveryMode()">
<md-input-container class="md-block" flex="flex" ng-if="!(app.isInPasswordRecoveryMode())">
<label><var:string label:value="Current password"/>
</label>
<input type="password" sg-no-dirty-check="true" ng-model="app.passwords.oldPassword"/>
@@ -232,6 +233,13 @@
<label><var:string label:value="New password"/>
</label>
<input type="password" sg-no-dirty-check="true" ng-model="app.passwords.newPassword"/>
<div class="sg-hint">
<ul class="sg-padded">
<li ng-repeat="item in app.passwordPolicy">
{{ item.label }}
</li>
</ul>
</div>
</md-input-container>
<md-input-container class="md-block" flex="flex">
<label><var:string label:value="Confirmation"/>

View File

@@ -149,12 +149,20 @@
perr == passwordPolicyConfig.PolicyChangeAfterReset) {
response = {
passwordexpired: 1,
userPolicies: data.additionalInfos.userPolicies,
url: redirectUrl(username, domain)
};
}
else if (perr == passwordPolicyConfig.PolicyChangeAfterReset) {
response = {
passwordexpired: 1,
userPolicies: data.additionalInfos.userPolicies,
url: redirectUrl(username, domain)
};
} else if (perr == passwordPolicyConfig.PolicyInsufficientPasswordQuality) {
response = {
passwordexpired: 2,
userPolicies: data.additionalInfos.userPolicies,
url: redirectUrl(username, domain)
};
}

View File

@@ -1,6 +1,6 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* JavaScript for MainUI (SOGoRootPage) */
(function() {
(function () {
'use strict';
angular.module('SOGo.MainUI', ['SOGo.Common', 'SOGo.Authentication']);
@@ -9,8 +9,8 @@
/**
* @ngInject
*/
LoginController.$inject = ['$scope', '$window', '$timeout', 'Dialog', '$mdDialog', 'Authentication', 'sgFocus', 'sgRippleClick'];
function LoginController($scope, $window, $timeout, Dialog, $mdDialog, Authentication, focus, rippleDo) {
LoginController.$inject = ['$scope', '$window', '$timeout', 'Dialog', '$mdDialog', 'Authentication', 'sgFocus', 'sgRippleClick', 'sgConstant', '$mdToast'];
function LoginController($scope, $window, $timeout, Dialog, $mdDialog, Authentication, focus, rippleDo, sgConstant, $mdToast) {
var vm = this;
this.$onInit = function () {
@@ -72,7 +72,7 @@
vm.showLogin = false;
rippleDo('loginContent');
}
} else {
vm.retrievePasswordRecoveryEnabled();
}
@@ -83,14 +83,14 @@
el.parentElement.classList.add("md-input-has-value");
});
}
}, 100);
}, 100);
};
this.login = function() {
this.login = function () {
vm.loginState = 'authenticating';
Authentication.login(vm.creds)
.then(function(data) {
.then(function (data) {
if (data.totpmissingkey) {
vm.loginState = 'totpcode';
@@ -107,11 +107,11 @@
vm.url = data.url;
// Let the user see the succesfull message before reloading the page
$timeout(function() {
$timeout(function () {
vm.continueLogin();
}, 1000);
}
}, function(msg) {
}, function (msg) {
vm.loginState = 'error';
if (msg.error) {
@@ -122,21 +122,22 @@
vm.loginState = 'passwordwillexpire';
vm.cn = msg.cn;
vm.url = msg.url;
vm.passwordPolicy = msg.userPolicies ? msg.userPolicies : [];
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);
value = Math.round(msg.expire / 86400);
string = l("days");
}
else if (msg.expire > 3600) {
value = Math.round(msg.expire/3600);
value = Math.round(msg.expire / 3600);
string = l("hours");
}
else if (msg.expire > 60) {
value = Math.round(msg.expire/60);
value = Math.round(msg.expire / 60);
string = l("minutes");
}
else {
@@ -148,34 +149,40 @@
vm.url = msg.url;
vm.errorMessage = l('Your password is going to expire in %{0} %{1}.', value, string);
}
else if (msg.passwordexpired) {
else if (msg.passwordexpired && msg.passwordexpired == 2) {
vm.loginState = 'passwordchange';
vm.passwordPolicy = msg.userPolicies ? msg.userPolicies : [];
vm.url = msg.url;
vm.passwordexpired = msg.passwordexpired;
} else if (msg.passwordexpired) {
vm.loginState = 'passwordchange';
vm.passwordPolicy = msg.userPolicies ? msg.userPolicies : [];
vm.url = msg.url;
vm.passwordexpired = msg.passwordexpired;
}
});
return false;
};
this.restoreLogin = function() {
vm.showLogin = false;
this.restoreLogin = function () {
if ('SecretQuestion' === vm.passwordRecovery.passwordRecoveryMode) {
rippleDo('loginContent');
vm.passwordRecoveryInfo();
vm.passwordRecoveryAbort();
} else {
delete vm.creds.verificationCode;
vm.passwordRecoveryAbort();
}
};
this.continueLogin = function() {
this.continueLogin = function () {
if ($window.location.href === vm.url)
$window.location.reload(true);
else
$window.location.href = vm.url;
};
this.showAbout = function($event) {
this.showAbout = function ($event) {
$mdDialog.show({
targetEvent: $event,
templateUrl: 'aboutBox.html',
@@ -184,24 +191,20 @@
});
AboutDialogController.$inject = ['$mdDialog'];
function AboutDialogController($mdDialog) {
this.closeDialog = function() {
this.closeDialog = function () {
$mdDialog.hide();
};
}
};
this.changeLanguage = function($event) {
this.changeLanguage = function ($event) {
// Reload page
$window.location.href = ApplicationBaseURL + 'login?language=' + this.creds.language;
};
this.hello = function (form) {
return !true;
}
this.canChangePassword = function(form) {
this.canChangePassword = function (form) {
if (this.passwords.newPasswordConfirmation && this.passwords.newPasswordConfirmation.length &&
this.passwords.newPassword != this.passwords.newPasswordConfirmation) {
this.passwords.newPassword != this.passwords.newPasswordConfirmation) {
form.newPasswordConfirmation.$setValidity('newPasswordMismatch', false);
return false;
}
@@ -209,25 +212,29 @@
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.isInPasswordRecoveryMode()) ||
(!this.loginState && this.passwords.oldPassword && this.passwords.oldPassword.length > 0) ||
this.passwords.newPasswordConfirmation && this.passwords.newPasswordConfirmation.length &&
this.passwords.newPassword == this.passwords.newPasswordConfirmation &&
((this.isInPasswordRecoveryMode()) ||
(!this.loginState && this.passwords.oldPassword && this.passwords.oldPassword.length > 0) ||
('passwordchange' == this.loginState && 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, this.passwordRecovery.passwordRecoveryToken).then(function(data) {
this.changePassword = function () {
Authentication.changePassword(this.creds.username, this.creds.domain, this.passwords.newPassword, this.passwords.oldPassword, this.passwordRecovery.passwordRecoveryToken).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;
}, function (msg) {
$mdToast.show(
$mdToast.simple()
.textContent(msg)
.position(sgConstant.toastPosition)
.hideDelay(2000)
);
});
};
@@ -260,32 +267,36 @@
, this.passwordRecovery.passwordRecoveryMailDomain).then(function () {
vm.loginState = 'sendrecoverymail';
vm.passwordRecovery.showLoader = false;
}, function (msg) {
vm.loginState = 'error';
vm.errorMessage = msg;
}, function (msg) {
vm.loginState = 'error';
vm.errorMessage = msg;
vm.passwordRecovery.showLoader = false;
});
});
};
this.passwordRecoveryCheck = function () {
vm.passwordRecovery.showLoader = true;
Authentication.passwordRecoveryCheck(this.creds.username, this.creds.domain
, this.passwordRecovery.passwordRecoveryMode
, this.passwordRecovery.passwordRecoveryQuestionKey
, this.passwordRecovery.passwordRecoveryQuestionAnswer
, this.passwordRecovery.passwordRecoveryMailDomain).then(function (token) {
if ("SecretQuestion" == vm.passwordRecovery.passwordRecoveryMode) {
vm.passwordRecovery.passwordRecoveryToken = token;
vm.loginState = 'passwordchange';
} else if ("SecondaryEmail" == vm.passwordRecovery.passwordRecoveryMode) {
vm.loginState = 'sendrecoverymail';
}
vm.passwordRecovery.showLoader = false;
}, function (msg) {
vm.loginState = 'error';
vm.errorMessage = msg;
vm.passwordRecovery.showLoader = false;
});
, this.passwordRecovery.passwordRecoveryMode
, this.passwordRecovery.passwordRecoveryQuestionKey
, this.passwordRecovery.passwordRecoveryQuestionAnswer
, this.passwordRecovery.passwordRecoveryMailDomain).then(function (token) {
if ("SecretQuestion" == vm.passwordRecovery.passwordRecoveryMode) {
vm.passwordRecovery.passwordRecoveryToken = token;
vm.loginState = 'passwordchange';
} else if ("SecondaryEmail" == vm.passwordRecovery.passwordRecoveryMode) {
vm.loginState = 'sendrecoverymail';
}
vm.passwordRecovery.showLoader = false;
}, function (msg) {
vm.loginState = 'error';
vm.errorMessage = msg;
vm.passwordRecovery.showLoader = false;
});
};
this.isPasswordExpiredSecurity = function () {
return (this.passwordexpired && 2 === this.passwordexpired);
};
this.isInPasswordRecoveryMode = function () {

View File

@@ -18,6 +18,8 @@ $sg-login-width: grid-step(5);
.sg-logo {
// Center image
margin: auto;
margin-top: 60px;
margin-bottom: 60px;
img {
height: 100%;
width: grid-step(5);