mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-04-04 21:08:51 +00:00
feat(preferences): password constraints for SQL sources
Initial implementation of some password policy support for SQL sources.
This commit is contained in:
@@ -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`,
|
||||
|
||||
@@ -405,6 +405,11 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
|
||||
return _searchFields;
|
||||
}
|
||||
|
||||
- (NSArray *) userPasswordPolicy
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void) setContactMapping: (NSDictionary *) theMapping
|
||||
andObjectClasses: (NSArray *) theObjectClasses
|
||||
{
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
- (NSString *) domain;
|
||||
- (NSArray *) searchFields;
|
||||
- (NSArray *) userPasswordPolicy;
|
||||
|
||||
- (id) connection;
|
||||
- (void) releaseConnection: (id) connection;
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
NSString *_imapLoginField;
|
||||
NSString *_imapHostField;
|
||||
NSString *_sieveHostField;
|
||||
NSArray *_userPasswordPolicy;
|
||||
NSString *_userPasswordAlgorithm;
|
||||
NSString *_keyPath;
|
||||
NSURL *_viewURL;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
//
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user