KEYCLOAK-886 Admin console - step 1

This commit is contained in:
mposolda 2015-05-22 21:05:47 +02:00
parent 490b3e3603
commit 319f86e91a
7 changed files with 70 additions and 23 deletions

View file

@ -108,8 +108,8 @@ public class LDAPConfig {
return searchScope == null ? SearchControls.SUBTREE_SCOPE : Integer.parseInt(searchScope); return searchScope == null ? SearchControls.SUBTREE_SCOPE : Integer.parseInt(searchScope);
} }
public String getUuidAttributeName() { public String getUuidLDAPAttributeName() {
String uuidAttrName = config.get(LDAPConstants.UUID_ATTRIBUTE_NAME); String uuidAttrName = config.get(LDAPConstants.UUID_LDAP_ATTRIBUTE);
if (uuidAttrName == null) { if (uuidAttrName == null) {
// Differences of unique attribute among various vendors // Differences of unique attribute among various vendors
String vendor = getVendor(); String vendor = getVendor();

View file

@ -1,11 +1,9 @@
package org.keycloak.federation.ldap.idm.store.ldap; package org.keycloak.federation.ldap.idm.store.ldap;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
@ -123,7 +121,7 @@ public class LDAPIdentityStore implements IdentityStore {
for (Condition condition : identityQuery.getConditions()) { for (Condition condition : identityQuery.getConditions()) {
// Check if we are searching by ID // Check if we are searching by ID
String uuidAttrName = getConfig().getUuidAttributeName(); String uuidAttrName = getConfig().getUuidLDAPAttributeName();
if (condition.getParameter() != null && condition.getParameter().getName().equals(uuidAttrName)) { if (condition.getParameter() != null && condition.getParameter().getName().equals(uuidAttrName)) {
if (EqualCondition.class.isInstance(condition)) { if (EqualCondition.class.isInstance(condition)) {
EqualCondition equalCondition = (EqualCondition) condition; EqualCondition equalCondition = (EqualCondition) condition;
@ -280,7 +278,7 @@ public class LDAPIdentityStore implements IdentityStore {
QueryParameter queryParameter = condition.getParameter(); QueryParameter queryParameter = condition.getParameter();
if (!getConfig().getUuidAttributeName().equals(queryParameter.getName())) { if (!getConfig().getUuidLDAPAttributeName().equals(queryParameter.getName())) {
String attributeName = queryParameter.getName(); String attributeName = queryParameter.getName();
if (attributeName != null) { if (attributeName != null) {
@ -400,7 +398,7 @@ public class LDAPIdentityStore implements IdentityStore {
String ldapAttributeName = ldapAttribute.getID(); String ldapAttributeName = ldapAttribute.getID();
if (ldapAttributeName.toLowerCase().equals(getConfig().getUuidAttributeName().toLowerCase())) { if (ldapAttributeName.toLowerCase().equals(getConfig().getUuidLDAPAttributeName().toLowerCase())) {
Object uuidValue = ldapAttribute.get(); Object uuidValue = ldapAttribute.get();
ldapObject.setUuid(this.operationManager.decodeEntryUUID(uuidValue)); ldapObject.setUuid(this.operationManager.decodeEntryUUID(uuidValue));
} else { } else {
@ -502,6 +500,9 @@ public class LDAPIdentityStore implements IdentityStore {
if (!ldapObject.getReadOnlyAttributeNames().contains(attrName) && (isCreate || !ldapObject.getRdnAttributeName().equals(attrName))) { if (!ldapObject.getReadOnlyAttributeNames().contains(attrName) && (isCreate || !ldapObject.getRdnAttributeName().equals(attrName))) {
if (String.class.isInstance(attrValue)) { if (String.class.isInstance(attrValue)) {
if (attrValue.toString().trim().length() == 0) {
attrValue = LDAPConstants.EMPTY_ATTRIBUTE_VALUE;
}
entryAttributes.put(attrName, attrValue); entryAttributes.put(attrName, attrValue);
} else if (Collection.class.isInstance(attrValue)) { } else if (Collection.class.isInstance(attrValue)) {
BasicAttribute attr = new BasicAttribute(attrName); BasicAttribute attr = new BasicAttribute(attrName);
@ -616,9 +617,9 @@ public class LDAPIdentityStore implements IdentityStore {
protected String getEntryIdentifier(final LDAPObject ldapObject) { protected String getEntryIdentifier(final LDAPObject ldapObject) {
try { try {
// we need this to retrieve the entry's identifier from the ldap server // we need this to retrieve the entry's identifier from the ldap server
String uuidAttrName = getConfig().getUuidAttributeName(); String uuidAttrName = getConfig().getUuidLDAPAttributeName();
List<SearchResult> search = this.operationManager.search(ldapObject.getDn().toString(), "(" + ldapObject.getDn().getFirstRdn() + ")", Arrays.asList(uuidAttrName), SearchControls.OBJECT_SCOPE); List<SearchResult> search = this.operationManager.search(ldapObject.getDn().toString(), "(" + ldapObject.getDn().getFirstRdn() + ")", Arrays.asList(uuidAttrName), SearchControls.OBJECT_SCOPE);
Attribute id = search.get(0).getAttributes().get(getConfig().getUuidAttributeName()); Attribute id = search.get(0).getAttributes().get(getConfig().getUuidLDAPAttributeName());
if (id == null) { if (id == null) {
throw new ModelException("Could not retrieve identifier for entry [" + ldapObject.getDn().toString() + "]."); throw new ModelException("Could not retrieve identifier for entry [" + ldapObject.getDn().toString() + "].");

View file

@ -419,7 +419,7 @@ public class LDAPOperationManager {
} }
private String getUuidAttributeName() { private String getUuidAttributeName() {
return this.config.getUuidAttributeName(); return this.config.getUuidLDAPAttributeName();
} }
public Attributes getAttributes(final String entryUUID, final String baseDN, Set<String> returningAttributes) { public Attributes getAttributes(final String entryUUID, final String baseDN, Set<String> returningAttributes) {

View file

@ -542,6 +542,7 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications,
instance.config.useKerberosForPasswordAuthentication = false; instance.config.useKerberosForPasswordAuthentication = false;
instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE; instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE;
instance.config.searchScope = "1";
$scope.fullSyncEnabled = false; $scope.fullSyncEnabled = false;
$scope.changedSyncEnabled = false; $scope.changedSyncEnabled = false;
@ -558,6 +559,9 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications,
if (!instance.config.batchSizeForSync) { if (!instance.config.batchSizeForSync) {
instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE; instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE;
} }
if (!instance.config.searchScope) {
instance.config.searchScope = "1";
}
$scope.fullSyncEnabled = (instance.fullSyncPeriod && instance.fullSyncPeriod > 0); $scope.fullSyncEnabled = (instance.fullSyncPeriod && instance.fullSyncPeriod > 0);
$scope.changedSyncEnabled = (instance.changedSyncPeriod && instance.changedSyncPeriod > 0); $scope.changedSyncEnabled = (instance.changedSyncPeriod && instance.changedSyncPeriod > 0);
} }
@ -577,6 +581,11 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications,
{ "id": "other", "name": "Other" } { "id": "other", "name": "Other" }
]; ];
$scope.searchScopes = [
{ "id": "1", "name": "One Level" },
{ "id": "2", "name": "Subtree" }
];
$scope.realm = realm; $scope.realm = realm;
$scope.$watch('fullSyncEnabled', function(newVal, oldVal) { $scope.$watch('fullSyncEnabled', function(newVal, oldVal) {
@ -607,12 +616,23 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications,
$scope.lastVendor = $scope.instance.config.vendor; $scope.lastVendor = $scope.instance.config.vendor;
if ($scope.lastVendor === "ad") { if ($scope.lastVendor === "ad") {
$scope.instance.config.usernameLDAPAttribute = "sAMAccountName"; $scope.instance.config.usernameLDAPAttribute = "cn";
$scope.instance.config.userObjectClasses = "person, organizationalPerson, user"; $scope.instance.config.userObjectClasses = "person, organizationalPerson, user";
} else { } else {
$scope.instance.config.usernameLDAPAttribute = "uid"; $scope.instance.config.usernameLDAPAttribute = "uid";
$scope.instance.config.userObjectClasses = "inetOrgPerson, organizationalPerson"; $scope.instance.config.userObjectClasses = "inetOrgPerson, organizationalPerson";
} }
$scope.instance.config.rdnLDAPAttribute = $scope.instance.config.usernameLDAPAttribute;
var vendorToUUID = {
rhds: "nsuniqueid",
tivoli: "uniqueidentifier",
edirectory: "guid",
ad: "objectGUID",
other: "entryUUID"
};
$scope.instance.config.uuidLDAPAttribute = vendorToUUID[$scope.lastVendor];
} }
}, true); }, true);

View file

@ -69,10 +69,24 @@
<div class="form-group clearfix"> <div class="form-group clearfix">
<label class="col-md-2 control-label" for="usernameLDAPAttribute"><span class="required">*</span> Username LDAP attribute</label> <label class="col-md-2 control-label" for="usernameLDAPAttribute"><span class="required">*</span> Username LDAP attribute</label>
<div class="col-md-6"> <div class="col-md-6">
<input class="form-control" id="usernameLDAPAttribute" type="text" ng-model="instance.config.usernameLDAPAttribute" placeholder="LDAP attribute for uid" required> <input class="form-control" id="usernameLDAPAttribute" type="text" ng-model="instance.config.usernameLDAPAttribute" placeholder="LDAP attribute name for username" required>
</div> </div>
<kc-tooltip>Name of LDAP attribute, which is mapped as Keycloak username. For many LDAP server vendors it's 'uid'. For Active directory it's usually 'sAMAccountName' or 'cn'</kc-tooltip> <kc-tooltip>Name of LDAP attribute, which is mapped as Keycloak username. For many LDAP server vendors it's 'uid'. For Active directory it's usually 'sAMAccountName' or 'cn'</kc-tooltip>
</div> </div>
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="rdnLDAPAttribute"><span class="required">*</span> RDN LDAP attribute</label>
<div class="col-md-6">
<input class="form-control" id="rdnLDAPAttribute" type="text" ng-model="instance.config.rdnLDAPAttribute" placeholder="LDAP attribute name for user RDN" required>
</div>
<kc-tooltip>Name of LDAP attribute, which is used as RDN (top attribute) of typical user DN. Usually it's the same as Username LDAP attribute, however for Active directory it could be 'cn' when username attribute might be 'sAMAccountName' </kc-tooltip>
</div>
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="uuidLDAPAttribute"><span class="required">*</span> UUID LDAP attribute</label>
<div class="col-md-6">
<input class="form-control" id="uuidLDAPAttribute" type="text" ng-model="instance.config.uuidLDAPAttribute" placeholder="LDAP attribute name for UUID" required>
</div>
<kc-tooltip>Name of LDAP attribute, which is used as unique object identifier (UUID) for objects in LDAP. For many LDAP server vendors it's 'entryUUID' however some are different. For example for Active directory it should be 'objectGUID' </kc-tooltip>
</div>
<div class="form-group clearfix"> <div class="form-group clearfix">
<label class="col-md-2 control-label" for="userObjectClasses"><span class="required">*</span> User Object Classes</label> <label class="col-md-2 control-label" for="userObjectClasses"><span class="required">*</span> User Object Classes</label>
<div class="col-md-6"> <div class="col-md-6">
@ -91,18 +105,13 @@
</div> </div>
</div> </div>
<div class="form-group clearfix"> <div class="form-group clearfix">
<label class="col-md-2 control-label" for="ldapBaseDn"><span class="required">*</span> Base DN</label> <label class="col-md-2 control-label" for="ldapUserDns"><span class="required">*</span> User DN</label>
<div class="col-md-6"> <div class="col-md-6">
<input class="form-control" id="ldapBaseDn" type="text" ng-model="instance.config.baseDn" placeholder="LDAP Base DN" required> <input class="form-control" id="ldapUserDns" type="text" ng-model="instance.config.userDns" placeholder="LDAP User DN" required>
</div> </div>
<kc-tooltip>Base DN of LDAP tree where your data are. Base DN is usually ancestor of User DN Suffix</kc-tooltip> <kc-tooltip>Full DN of LDAP tree where your users are. This DN is parent of LDAP users. It could be for example 'ou=users,dc=example,dc=com' assuming
</div> that your typical user will have DN like 'uid=john,ou=users,dc=example,dc=com'
<div class="form-group clearfix"> </kc-tooltip>
<label class="col-md-2 control-label" for="ldapUserDnSuffix"><span class="required">*</span> User DN Suffix</label>
<div class="col-md-6">
<input class="form-control" id="ldapUserDnSuffix" type="text" ng-model="instance.config.userDnSuffix" placeholder="LDAP User DN Suffix" required>
</div>
<kc-tooltip>Base DN of LDAP tree where your users are. This DN is parent of all DNs of LDAP users</kc-tooltip>
</div> </div>
<div class="form-group clearfix"> <div class="form-group clearfix">
<label class="col-md-2 control-label" for="ldapBindDn"><span class="required">*</span> Bind DN</label> <label class="col-md-2 control-label" for="ldapBindDn"><span class="required">*</span> Bind DN</label>
@ -121,6 +130,19 @@
<a class="btn btn-primary" data-ng-click="testAuthentication()">Test authentication</a> <a class="btn btn-primary" data-ng-click="testAuthentication()">Test authentication</a>
</div> </div>
</div> </div>
<div class="form-group">
<label class="col-md-2 control-label" for="searchScope">Search scope</label>
<div class="col-md-6">
<div>
<select class="form-control" id="searchScope"
ng-model="instance.config.searchScope"
ng-options="searchScope.id as searchScope.name for searchScope in searchScopes"
required>
</select>
</div>
</div>
<kc-tooltip>For one level, we search for users just in DNs specified by User DNs. For subtree, we search in whole of their subtree. See LDAP documentation for more details</kc-tooltip>
</div>
<div class="form-group clearfix"> <div class="form-group clearfix">
<label class="col-md-2 control-label" for="connectionPooling">Connection pooling</label> <label class="col-md-2 control-label" for="connectionPooling">Connection pooling</label>
<div class="col-md-6"> <div class="col-md-6">

View file

@ -14,6 +14,7 @@ public class LDAPConstants {
public static final String USERNAME_LDAP_ATTRIBUTE = "usernameLDAPAttribute"; public static final String USERNAME_LDAP_ATTRIBUTE = "usernameLDAPAttribute";
public static final String RDN_LDAP_ATTRIBUTE = "rdnLDAPAttribute"; public static final String RDN_LDAP_ATTRIBUTE = "rdnLDAPAttribute";
public static final String UUID_LDAP_ATTRIBUTE = "uuidLDAPAttribute";
public static final String USER_OBJECT_CLASSES = "userObjectClasses"; public static final String USER_OBJECT_CLASSES = "userObjectClasses";
public static final String CONNECTION_URL = "connectionUrl"; public static final String CONNECTION_URL = "connectionUrl";
@ -23,7 +24,6 @@ public class LDAPConstants {
public static final String BIND_CREDENTIAL = "bindCredential"; public static final String BIND_CREDENTIAL = "bindCredential";
public static final String SEARCH_SCOPE = "searchScope"; public static final String SEARCH_SCOPE = "searchScope";
public static final String UUID_ATTRIBUTE_NAME = "uuidAttributeName";
public static final String CONNECTION_POOLING = "connectionPooling"; public static final String CONNECTION_POOLING = "connectionPooling";
public static final String PAGINATION = "pagination"; public static final String PAGINATION = "pagination";

View file

@ -167,6 +167,10 @@ public class KeycloakServer {
System.setProperty("keycloak.theme.cacheTemplates", "false"); System.setProperty("keycloak.theme.cacheTemplates", "false");
} }
if (!System.getProperties().containsKey("keycloak.theme.staticMaxAge")) {
System.setProperty("keycloak.theme.staticMaxAge", "-1");
}
config.setResourcesHome(dir.getAbsolutePath()); config.setResourcesHome(dir.getAbsolutePath());
} }