feat(preferences): password constraints for SQL sources

Initial implementation of some password policy support for SQL sources.
This commit is contained in:
Francis Lachapelle
2022-06-06 16:39:26 -04:00
parent 03cdc4f3b1
commit 2ef849ca65
7 changed files with 99 additions and 4 deletions

View File

@@ -1660,6 +1660,39 @@ Other columns can exist and will actually be mapped automatically if
they have the same name as popular LDAP attributes (such as `givenName`,
`sn`, `department`, `title`, `telephoneNumber`, etc.).
|userPasswordPolicy
|An array of dictionaries that define regular expressions used to determine whether a new password
is valid.
Each dictionary must contain the key "regex" associated to a string representing a regular
expression. It can also contain the key "label" to briefly describe the constraint to the user. Example:
----
userPasswordPolicy = (
{
label = "Minimum of 1 lowercase letter";
regex = "[a-z]";
},
{
label = "Minimum of 1 uppercase letter";
regex = "[A-Z]";
},
{
label = "Minimum of 1 digit";
regex = "[0-9]";
},
{
label = "Minimum of 2 special symbols";
regex = "([%$&*(){}!?\@#].*){2,}";
},
{
label = "Minimum length of 8 characters";
regex = ".{8,}";
}
);
----
|userPasswordAlgorithm
|The default algorithm used for password encryption when changing
passwords. Possible values are: `none`, `plain`, `crypt`, `md5`,

View File

@@ -405,6 +405,11 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
return _searchFields;
}
- (NSArray *) userPasswordPolicy
{
return nil;
}
- (void) setContactMapping: (NSDictionary *) theMapping
andObjectClasses: (NSArray *) theObjectClasses
{

View File

@@ -40,6 +40,7 @@
- (NSString *) domain;
- (NSArray *) searchFields;
- (NSArray *) userPasswordPolicy;
- (id) connection;
- (void) releaseConnection: (id) connection;

View File

@@ -41,6 +41,7 @@
NSString *_imapLoginField;
NSString *_imapHostField;
NSString *_sieveHostField;
NSArray *_userPasswordPolicy;
NSString *_userPasswordAlgorithm;
NSString *_keyPath;
NSURL *_viewURL;

View File

@@ -98,6 +98,7 @@
// "mail" expands to all entries of MailFieldNames
_searchFields = [NSArray arrayWithObjects: @"c_cn", @"mail", nil];
[_searchFields retain];
_userPasswordPolicy = nil;
_userPasswordAlgorithm = nil;
_keyPath = nil;
_viewURL = nil;
@@ -119,6 +120,7 @@
[_loginFields release];
[_mailFields release];
[_searchFields release];
[_userPasswordPolicy release];
[_userPasswordAlgorithm release];
[_keyPath release];
[_viewURL release];
@@ -143,6 +145,7 @@
ASSIGN(_authenticationFilter, [udSource objectForKey: @"authenticationFilter"]);
ASSIGN(_loginFields, [udSource objectForKey: @"LoginFieldNames"]);
ASSIGN(_mailFields, [udSource objectForKey: @"MailFieldNames"]);
ASSIGN(_userPasswordPolicy, [udSource objectForKey: @"userPasswordPolicy"]);
ASSIGN(_userPasswordAlgorithm, [udSource objectForKey: @"userPasswordAlgorithm"]);
ASSIGN(_keyPath, [udSource objectForKey: @"keyPath"]);
ASSIGN(_imapLoginField, [udSource objectForKey: @"IMAPLoginFieldName"]);
@@ -192,6 +195,11 @@
return _searchFields;
}
- (NSArray *) userPasswordPolicy
{
return _userPasswordPolicy;
}
- (BOOL) _isPassword: (NSString *) plainPassword
equalTo: (NSString *) encryptedPassword
{
@@ -328,7 +336,7 @@
* @param login the user's login name.
* @param oldPassword the previous password.
* @param newPassword the new password.
* @param perr is not used.
* @param perr will be set if the new password is not conform to the policy.
* @return YES if the password was successfully changed.
*/
- (BOOL) changePasswordForLogin: (NSString *) login
@@ -336,20 +344,48 @@
newPassword: (NSString *) newPassword
perr: (SOGoPasswordPolicyError *) perr
{
BOOL didChange, isOldPwdOk, isPolicyOk;
EOAdaptorChannel *channel;
GCSChannelManager *cm;
NSDictionary *policy;
NSEnumerator *policies;
NSException *ex;
NSString *sqlstr;
BOOL didChange;
BOOL isOldPwdOk;
NSRange match;
NSString *sqlstr, *regex;
*perr = -1;
isOldPwdOk = NO;
isPolicyOk = YES;
didChange = NO;
// Verify current password
isOldPwdOk = [self checkLogin:login password:oldPassword perr:perr expire:0 grace:0];
if (isOldPwdOk)
{
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];
}
}
}
if (isOldPwdOk && isPolicyOk)
{
// Encrypt new password
NSString *encryptedPassword = [self _encryptPassword: newPassword];

View File

@@ -38,6 +38,7 @@
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/SOGoSieveManager.h>
#import <SOGo/SOGoSource.h>
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/SOGoUserFolder.h>
#import <SOGo/SOGoParentFolder.h>
@@ -250,6 +251,17 @@ static NSArray *reminderValues = nil;
return shortDateFormatText;
}
//
// Used by wox template
//
- (NSArray *) passwordPolicy
{
NSObject <SOGoSource> *userSource;
userSource = [user authenticationSource];
return [userSource userPasswordPolicy];
}
//
// Used internally
//

View File

@@ -282,6 +282,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">
<var:foreach list="passwordPolicy" item="item">
<li><var:string value="item.label"/></li>
</var:foreach>
</ul>
</div>
</md-input-container>
<md-input-container class="md-block" flex="flex">
<label><var:string label:value="Confirmation"/>