KEYCLOAK-271 Improvements to password policies in admin console
This commit is contained in:
parent
427a30f644
commit
5160f02475
3 changed files with 91 additions and 134 deletions
|
@ -256,7 +256,7 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $ht
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('RealmRequiredCredentialsCtrl', function($scope, Realm, realm, $http, $location, Dialog, Notifications) {
|
module.controller('RealmRequiredCredentialsCtrl', function($scope, Realm, realm, $http, $location, Dialog, Notifications, PasswordPolicy) {
|
||||||
console.log('RealmRequiredCredentialsCtrl');
|
console.log('RealmRequiredCredentialsCtrl');
|
||||||
|
|
||||||
$scope.realm = {
|
$scope.realm = {
|
||||||
|
@ -264,128 +264,25 @@ module.controller('RealmRequiredCredentialsCtrl', function($scope, Realm, realm,
|
||||||
requiredCredentials : realm.requiredCredentials,
|
requiredCredentials : realm.requiredCredentials,
|
||||||
requiredApplicationCredentials : realm.requiredApplicationCredentials,
|
requiredApplicationCredentials : realm.requiredApplicationCredentials,
|
||||||
requiredOAuthClientCredentials : realm.requiredOAuthClientCredentials,
|
requiredOAuthClientCredentials : realm.requiredOAuthClientCredentials,
|
||||||
registrationAllowed : realm.registrationAllowed
|
registrationAllowed : realm.registrationAllowed,
|
||||||
|
passwordPolicy: realm.passwordPolicy
|
||||||
};
|
};
|
||||||
|
|
||||||
if (realm.hasOwnProperty('passwordPolicy')){
|
|
||||||
$scope.realm['passwordPolicy'] = realm.passwordPolicy;
|
|
||||||
} else {
|
|
||||||
$scope.realm['passwordPolicy'] = "";
|
|
||||||
realm['passwordPolicy'] = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
var oldCopy = angular.copy($scope.realm);
|
var oldCopy = angular.copy($scope.realm);
|
||||||
|
|
||||||
/* Map used in the table when hovering over (i) icon */
|
$scope.allPolicies = PasswordPolicy.allPolicies;
|
||||||
$scope.policyMessages = {
|
$scope.policyMessages = PasswordPolicy.policyMessages;
|
||||||
length: "Minimal password length (integer type). Default value is 8.",
|
|
||||||
digits: "Minimal number (integer type) of digits in password. Default value is 1.",
|
$scope.policy = PasswordPolicy.parse(realm.passwordPolicy);
|
||||||
lowerCase: "Minimal number (integer type) of lowercase characters in password. Default value is 1.",
|
|
||||||
upperCase: "Minimal number (integer type) of uppercase characters in password. Default value is 1.",
|
$scope.addPolicy = function(policy){
|
||||||
specialChars: "Minimal number (integer type) of special characters in password. Default value is 1."
|
$scope.policy.push(policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
// $scope.policy is an object representing passwordPolicy string
|
$scope.removePolicy = function(index){
|
||||||
$scope.policy = {};
|
$scope.policy.splice(index, 1);
|
||||||
// All available policies
|
|
||||||
$scope.allPolicies = ['length', 'digits', 'lowerCase', 'upperCase', 'specialChars'];
|
|
||||||
// List of configured policies
|
|
||||||
$scope.configuredPolicies = [];
|
|
||||||
// List of not configured policies
|
|
||||||
$scope.availablePolicies = $scope.allPolicies.slice(0);
|
|
||||||
|
|
||||||
$scope.addPolicy = function(){
|
|
||||||
$scope.policy[$scope.newPolicyId] = "";
|
|
||||||
updateConfigured();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.removePolicy = function(pId){
|
|
||||||
delete $scope.policy[pId];
|
|
||||||
updateConfigured();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updating lists of configured and non-configured policies based on the $scope.policy object
|
|
||||||
var updateConfigured = function(){
|
|
||||||
|
|
||||||
for (var i = 0; i < $scope.allPolicies.length; i++){
|
|
||||||
|
|
||||||
var policy = $scope.allPolicies[i];
|
|
||||||
|
|
||||||
if($scope.policy.hasOwnProperty(policy)){
|
|
||||||
|
|
||||||
var ind = $scope.configuredPolicies.indexOf(policy);
|
|
||||||
|
|
||||||
if(ind < 0){
|
|
||||||
$scope.configuredPolicies.push(policy);
|
|
||||||
}
|
|
||||||
|
|
||||||
ind = $scope.availablePolicies.indexOf(policy);
|
|
||||||
if(ind > -1){
|
|
||||||
$scope.availablePolicies.splice(ind, 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
var ind = $scope.configuredPolicies.indexOf(policy);
|
|
||||||
|
|
||||||
if(ind > -1){
|
|
||||||
$scope.configuredPolicies.splice(ind, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ind = $scope.availablePolicies.indexOf(policy);
|
|
||||||
if(ind < 0){
|
|
||||||
$scope.availablePolicies.push(policy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($scope.availablePolicies.length > 0){
|
|
||||||
$scope.newPolicyId = $scope.availablePolicies[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creating object from policy string
|
|
||||||
var evaluatePolicy = function(policyString){
|
|
||||||
|
|
||||||
var policyObject = {};
|
|
||||||
|
|
||||||
if (!policyString || policyString.length == 0){
|
|
||||||
return policyObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
var policyArray = policyString.split(" and ");
|
|
||||||
|
|
||||||
for (var i = 0; i < policyArray.length; i ++){
|
|
||||||
var policyToken = policyArray[i];
|
|
||||||
var re = /(\w+)\(*(\d*)\)*/;
|
|
||||||
|
|
||||||
var policyEntry = re.exec(policyToken);
|
|
||||||
policyObject[policyEntry[1]] = policyEntry[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
return policyObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creating policy string based on policy object
|
|
||||||
var generatePolicy = function(policyObject){
|
|
||||||
var policyString = "";
|
|
||||||
|
|
||||||
for (var key in policyObject){
|
|
||||||
policyString += key;
|
|
||||||
var value = policyObject[key];
|
|
||||||
if ( value != ""){
|
|
||||||
policyString += "("+value+")";
|
|
||||||
}
|
|
||||||
policyString += " and ";
|
|
||||||
}
|
|
||||||
|
|
||||||
policyString = policyString.substring(0, policyString.length - 5);
|
|
||||||
|
|
||||||
return policyString;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.policy = evaluatePolicy(realm.passwordPolicy);
|
|
||||||
updateConfigured();
|
|
||||||
|
|
||||||
$scope.userCredentialOptions = {
|
$scope.userCredentialOptions = {
|
||||||
'multiple' : true,
|
'multiple' : true,
|
||||||
'simple_tags' : true,
|
'simple_tags' : true,
|
||||||
|
@ -400,9 +297,9 @@ module.controller('RealmRequiredCredentialsCtrl', function($scope, Realm, realm,
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
$scope.$watch('policy', function() {
|
$scope.$watch('policy', function(oldVal, newVal) {
|
||||||
$scope.realm.passwordPolicy = generatePolicy($scope.policy);
|
if (oldVal != newVal) {
|
||||||
if ($scope.realm.passwordPolicy != realm.passwordPolicy){
|
$scope.realm.passwordPolicy = PasswordPolicy.toString($scope.policy);
|
||||||
$scope.changed = true;
|
$scope.changed = true;
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
@ -419,11 +316,8 @@ module.controller('RealmRequiredCredentialsCtrl', function($scope, Realm, realm,
|
||||||
|
|
||||||
$scope.reset = function() {
|
$scope.reset = function() {
|
||||||
$scope.realm = angular.copy(oldCopy);
|
$scope.realm = angular.copy(oldCopy);
|
||||||
|
$scope.policy = PasswordPolicy.parse(oldCopy.passwordPolicy);
|
||||||
$scope.configuredPolicies = [];
|
console.debug(realm.passwordPolicy);
|
||||||
$scope.availablePolicies = $scope.allPolicies.slice(0);
|
|
||||||
$scope.policy = evaluatePolicy(oldCopy.passwordPolicy);
|
|
||||||
updateConfigured();
|
|
||||||
|
|
||||||
$scope.changed = false;
|
$scope.changed = false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -386,3 +386,68 @@ module.factory('TimeUnit', function() {
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module.factory('PasswordPolicy', function() {
|
||||||
|
var p = {};
|
||||||
|
|
||||||
|
p.policyMessages = {
|
||||||
|
length: "Minimal password length (integer type). Default value is 8.",
|
||||||
|
digits: "Minimal number (integer type) of digits in password. Default value is 1.",
|
||||||
|
lowerCase: "Minimal number (integer type) of lowercase characters in password. Default value is 1.",
|
||||||
|
upperCase: "Minimal number (integer type) of uppercase characters in password. Default value is 1.",
|
||||||
|
specialChars: "Minimal number (integer type) of special characters in password. Default value is 1."
|
||||||
|
}
|
||||||
|
|
||||||
|
p.allPolicies = [
|
||||||
|
{ name: 'length', value: 8 },
|
||||||
|
{ name: 'digits', value: 1 },
|
||||||
|
{ name: 'lowerCase', value: 1 },
|
||||||
|
{ name: 'upperCase', value: 1 },
|
||||||
|
{ name: 'specialChars', value: 1 }
|
||||||
|
];
|
||||||
|
|
||||||
|
p.parse = function(policyString) {
|
||||||
|
var policies = [];
|
||||||
|
|
||||||
|
if (!policyString || policyString.length == 0){
|
||||||
|
return policies;
|
||||||
|
}
|
||||||
|
|
||||||
|
var policyArray = policyString.split(" and ");
|
||||||
|
|
||||||
|
for (var i = 0; i < policyArray.length; i ++){
|
||||||
|
var policyToken = policyArray[i];
|
||||||
|
var re = /(\w+)\(*(\d*)\)*/;
|
||||||
|
|
||||||
|
var policyEntry = re.exec(policyToken);
|
||||||
|
|
||||||
|
policies.push({ name: policyEntry[1], value: parseInt(policyEntry[2]) });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return policies;
|
||||||
|
};
|
||||||
|
|
||||||
|
p.toString = function(policies) {
|
||||||
|
if (!policies || policies.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var policyString = "";
|
||||||
|
|
||||||
|
for (var i in policies){
|
||||||
|
policyString += policies[i].name;
|
||||||
|
if ( policies[i].value ){
|
||||||
|
policyString += '(' + policies[i].value + ')';
|
||||||
|
}
|
||||||
|
policyString += " and ";
|
||||||
|
}
|
||||||
|
|
||||||
|
policyString = policyString.substring(0, policyString.length - 5);
|
||||||
|
|
||||||
|
return policyString;
|
||||||
|
};
|
||||||
|
|
||||||
|
return p;
|
||||||
|
});
|
|
@ -52,18 +52,16 @@
|
||||||
<table>
|
<table>
|
||||||
<caption class="hidden">Table of Password Policies</caption>
|
<caption class="hidden">Table of Password Policies</caption>
|
||||||
<thead>
|
<thead>
|
||||||
<tr ng-show="availablePolicies.length > 0">
|
<tr ng-show="(allPolicies|remove:policy:'name').length > 0">
|
||||||
<th colspan="5" class="rcue-table-actions">
|
<th colspan="5" class="rcue-table-actions">
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<div class="select-rcue">
|
<div class="select-rcue">
|
||||||
<select ng-model="newPolicyId"
|
<select ng-model="selectedPolicy"
|
||||||
ng-options="name as name for name in availablePolicies"
|
ng-options="p.name for p in (allPolicies|remove:policy:'name')"
|
||||||
placeholder="Please select">
|
data-ng-change="addPolicy(selectedPolicy); selectedAllPolicies = null">
|
||||||
|
<option value="" disabled selected>Add policy...</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<button ng-click="addPolicy()" ng-disabled="">Add Policy</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -74,19 +72,19 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="name in configuredPolicies">
|
<tr ng-repeat="p in policy">
|
||||||
<td>
|
<td>
|
||||||
<div class="clearfix">
|
<div class="clearfix">
|
||||||
<input class="input-small disabled" type="text" value="{{name}}" readonly>
|
<input class="input-small disabled" type="text" value="{{p.name}}" readonly>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input ng-model="policy[name]" type="number" placeholder="No value assigned" class="input-small">
|
<input ng-model="p.value" type="number" placeholder="No value assigned" class="input-small" min="1" max="50">
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="action-div"><i class="icon-question" popover="{{policyMessages[name]}}"
|
<div class="action-div"><i class="icon-question" popover="{{policyMessages[name]}}"
|
||||||
popover-placement="left" popover-trigger="mouseenter"></i></div>
|
popover-placement="left" popover-trigger="mouseenter"></i></div>
|
||||||
<div class="action-div"><i class="icon-remove" ng-click="removePolicy(name)"></i></div>
|
<div class="action-div"><i class="icon-remove" ng-click="removePolicy($index)"></i></div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
Loading…
Reference in a new issue