fix conflicts

This commit is contained in:
Bill Burke 2015-04-13 09:59:59 -04:00
commit f61fb4ba18
372 changed files with 12857 additions and 13271 deletions

View file

@ -52,6 +52,11 @@
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View file

@ -112,11 +112,22 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
}
protected String extractTokenFromResponse(String response, String tokenName) {
if(response == null)
return null;
if (response.startsWith("{")) {
try {
return mapper.readTree(response).get(tokenName).getTextValue();
JsonNode node = mapper.readTree(response);
if(node.has(tokenName)){
String s = node.get(tokenName).getTextValue();
if(s == null || s.trim().isEmpty())
return null;
return s;
} else {
return null;
}
} catch (IOException e) {
throw new IdentityBrokerException("Could not extract token [" + tokenName + "] from response [" + response + "].", e);
throw new IdentityBrokerException("Could not extract token [" + tokenName + "] from response [" + response + "] due: " + e.getMessage(), e);
}
} else {
Matcher matcher = Pattern.compile(tokenName + "=([^&]+)").matcher(response);
@ -133,7 +144,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
if (accessToken == null) {
throw new IdentityBrokerException("No access token from server.");
throw new IdentityBrokerException("No access token available in OAuth server response: " + response);
}
return doGetFederatedIdentity(accessToken);
@ -144,7 +155,6 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
return null;
}
;
protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) {
return UriBuilder.fromUri(getConfig().getAuthorizationUrl())
@ -155,9 +165,20 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
.queryParam(OAUTH2_PARAMETER_REDIRECT_URI, request.getRedirectUri());
}
/**
* Get JSON property as text. JSON numbers and booleans are converted to text. Empty string is converted to null.
*
* @param jsonNode to get property from
* @param name of property to get
* @return string value of the property or null.
*/
protected String getJsonProperty(JsonNode jsonNode, String name) {
if (jsonNode.has(name)) {
return jsonNode.get(name).asText();
if (jsonNode.has(name) && !jsonNode.get(name).isNull()) {
String s = jsonNode.get(name).asText();
if(s != null && !s.isEmpty())
return s;
else
return null;
}
return null;

View file

@ -4,14 +4,12 @@ import org.keycloak.broker.oidc.util.SimpleHttp;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.events.EventBuilder;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.adapters.action.AdminAction;
import org.keycloak.representations.adapters.action.LogoutAction;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.util.JsonSerialization;
import org.keycloak.util.PemUtils;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
@ -90,7 +88,7 @@ public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
@Override
public SimpleHttp generateTokenRequest(String authorizationCode) {
return super.generateTokenRequest(authorizationCode)
.param(AdapterConstants.APPLICATION_SESSION_STATE, "n/a"); // hack to get backchannel logout to work
.param(AdapterConstants.CLIENT_SESSION_STATE, "n/a"); // hack to get backchannel logout to work
}

View file

@ -0,0 +1,134 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
*/
package org.keycloak.broker.oidc;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.codehaus.jackson.JsonNode;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.FederatedIdentity;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.models.IdentityProviderModel;
/**
* Unit test for {@link AbstractOAuth2IdentityProvider}
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public class AbstractOAuth2IdentityProviderTest {
@Test
public void constructor_defaultScopeHandling(){
TestProvider tested = getTested();
//default scope is set from the provider if not provided in the configuration
Assert.assertEquals( tested.getDefaultScopes(), tested.getConfig().getDefaultScope());
//default scope is preserved if provided in the configuration
IdentityProviderModel model = new IdentityProviderModel();
OAuth2IdentityProviderConfig config = new OAuth2IdentityProviderConfig(model);
config.setDefaultScope("myscope");
tested = new TestProvider(config);
Assert.assertEquals("myscope", tested.getConfig().getDefaultScope());
}
@Test
public void getJsonProperty_asJsonNode() throws IOException {
TestProvider tested = getTested();
JsonNode jsonNode = tested
.asJsonNode("{\"nullone\":null, \"emptyone\":\"\", \"blankone\": \" \", \"withvalue\" : \"my value\", \"withbooleanvalue\" : true, \"withnumbervalue\" : 10}");
Assert.assertNull(tested.getJsonProperty(jsonNode, "nonexisting"));
Assert.assertNull(tested.getJsonProperty(jsonNode, "nullone"));
Assert.assertNull(tested.getJsonProperty(jsonNode, "emptyone"));
Assert.assertEquals(" ", tested.getJsonProperty(jsonNode, "blankone"));
Assert.assertEquals("my value", tested.getJsonProperty(jsonNode, "withvalue"));
Assert.assertEquals("true", tested.getJsonProperty(jsonNode, "withbooleanvalue"));
Assert.assertEquals("10", tested.getJsonProperty(jsonNode, "withnumbervalue"));
}
@Test(expected = IdentityBrokerException.class)
public void getFederatedIdentity_responseUrlLine_tokenNotFound() {
TestProvider tested = getTested();
tested.getFederatedIdentity("cosi=sss");
}
@Test(expected = IdentityBrokerException.class)
public void getFederatedIdentity_responseJSON_tokenNotFound() {
TestProvider tested = getTested();
Map<String, String> notes = new HashMap<>();
tested.getFederatedIdentity("{\"cosi\":\"sss\"}");
}
@Test(expected = IdentityBrokerException.class)
public void getFederatedIdentity_responseJSON_invalidFormat() {
TestProvider tested = getTested();
Map<String, String> notes = new HashMap<>();
tested.getFederatedIdentity("{\"cosi\":\"sss\"");
}
@Test(expected = IdentityBrokerException.class)
public void getFederatedIdentity_responseJSON_emptyTokenField() {
TestProvider tested = getTested();
tested.getFederatedIdentity("{\""
+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "\" : \"\"}");
}
@Test(expected = IdentityBrokerException.class)
public void getFederatedIdentity_responseJSON_nullTokenField() {
TestProvider tested = getTested();
tested.getFederatedIdentity("{\""
+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "\" : null}");
}
@Test
public void getFederatedIdentity_responseJSON() {
TestProvider tested = getTested();
BrokeredIdentityContext fi = tested.getFederatedIdentity("{\""
+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "\" : \"458rt\"}");
Assert.assertNotNull(fi);
Assert.assertEquals("458rt", fi.getId());
}
@Test
public void getFederatedIdentity_responseUrlLine() {
TestProvider tested = getTested();
BrokeredIdentityContext fi = tested.getFederatedIdentity("cosi=sss&"
+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "=458rtf&kdesi=ss}");
Assert.assertNotNull(fi);
Assert.assertEquals("458rtf", fi.getId());
}
private TestProvider getTested() {
IdentityProviderModel model = new IdentityProviderModel();
OAuth2IdentityProviderConfig config = new OAuth2IdentityProviderConfig(model);
return new TestProvider(config);
}
private static class TestProvider extends AbstractOAuth2IdentityProvider<OAuth2IdentityProviderConfig> {
public TestProvider(OAuth2IdentityProviderConfig config) {
super(config);
}
@Override
protected String getDefaultScopes() {
return "default";
}
protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
return new BrokeredIdentityContext(accessToken);
};
};
}

View file

@ -36,5 +36,17 @@
<addPrimaryKey columnNames="IDP_MAPPER_ID, NAME" constraintName="CONSTRAINT_IDPMConfig" tableName="IDP_MAPPER_CONFIG"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="IDENTITY_PROVIDER_MAPPER" constraintName="FK_IDPM_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="IDP_MAPPER_ID" baseTableName="IDP_MAPPER_CONFIG" constraintName="FK_IDPMConfig" referencedColumnNames="ID" referencedTableName="IDENTITY_PROVIDER_MAPPER"/>
<addColumn tableName="CLIENT">
<column name="CONSENT_REQUIRED" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
<update tableName="CLIENT">
<column name="CONSENT_REQUIRED" valueBoolean="true"/>
<where>DTYPE = 'OAuthClientEntity'</where>
</update>
<dropColumn tableName="CLIENT" columnName="DTYPE"/>
<renameColumn tableName="CLIENT" newColumnName="CLIENT_ID" oldColumnName="NAME"/>
</changeSet>
</databaseChangeLog>

View file

@ -3,9 +3,8 @@
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="keycloak-default" transaction-type="RESOURCE_LOCAL">
<class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
<class>org.keycloak.models.jpa.entities.ClientEntity</class>
<class>org.keycloak.models.jpa.entities.CredentialEntity</class>
<class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
<class>org.keycloak.models.jpa.entities.RealmEntity</class>
<class>org.keycloak.models.jpa.entities.RealmAttributeEntity</class>
<class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>

View file

@ -10,6 +10,7 @@ import org.keycloak.connections.mongo.updater.impl.updates.Update;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_0_0_Final;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_1_0_Beta1;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_2_0_Beta1;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_2_0_RC1;
import org.keycloak.models.KeycloakSession;
import java.util.Date;
@ -28,7 +29,8 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
private Class<? extends Update>[] updates = new Class[]{
Update1_0_0_Final.class,
Update1_1_0_Beta1.class,
Update1_2_0_Beta1.class
Update1_2_0_Beta1.class,
Update1_2_0_RC1.class
};
@Override

View file

@ -53,15 +53,8 @@ public abstract class Update {
log.debugv("Deleted entries from {0}", collection);
}
protected String insertApplicationRole(DBCollection roles, String roleName, String applicationId) {
BasicDBObject role = new BasicDBObject();
String roleId = KeycloakModelUtils.generateId();
role.append("_id", roleId);
role.append("name", roleName);
role.append("applicationId", applicationId);
role.append("nameIndex", applicationId + "//" + roleName);
roles.insert(role);
return roleId;
protected void renameCollection(String collection, String newName) {
db.getCollection(collection).rename(newName);
}
public void setLog(Logger log) {

View file

@ -266,4 +266,15 @@ public class Update1_2_0_Beta1 extends Update {
}
}
private String insertApplicationRole(DBCollection roles, String roleName, String applicationId) {
BasicDBObject role = new BasicDBObject();
String roleId = KeycloakModelUtils.generateId();
role.append("_id", roleId);
role.append("name", roleName);
role.append("applicationId", applicationId);
role.append("nameIndex", applicationId + "//" + roleName);
roles.insert(role);
return roleId;
}
}

View file

@ -0,0 +1,51 @@
package org.keycloak.connections.mongo.updater.impl.updates;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import org.keycloak.models.KeycloakSession;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class Update1_2_0_RC1 extends Update {
@Override
public String getId() {
return "1.2.0.RC1";
}
@Override
public void update(KeycloakSession session) {
convertApplicationsToClients();
convertOAuthClientsToClients();
}
private void convertApplicationsToClients() {
DBCollection applications = db.getCollection("applications");
applications.update(new BasicDBObject(), new BasicDBObject("$set", new BasicDBObject("consentRequired", false)), false, true);
applications.update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("name", "clientId")), false, true);
renameCollection("applications", "clients");
log.debugv("Converted applications to clients");
DBCollection roles = db.getCollection("roles");
roles.update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("applicationId", "clientId")), false, true);
log.debugv("Renamed roles.applicationId to roles.clientId");
}
private void convertOAuthClientsToClients() {
DBCollection clients = db.getCollection("clients");
DBCollection oauthClients = db.getCollection("oauthClients");
oauthClients.update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("name", "clientId")), false, true);
oauthClients.update(new BasicDBObject(), new BasicDBObject("$set", new BasicDBObject("consentRequired", true)), false, true);
DBCursor curs = oauthClients.find();
while (curs.hasNext()) {
clients.insert(curs.next());
}
oauthClients.drop();
log.debugv("Converted oauthClients to clients");
}
}

View file

@ -33,8 +33,7 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
"org.keycloak.models.entities.RequiredCredentialEntity",
"org.keycloak.models.entities.CredentialEntity",
"org.keycloak.models.entities.FederatedIdentityEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoClientEntity",
"org.keycloak.models.sessions.mongo.entities.MongoUsernameLoginFailureEntity",
"org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity",
"org.keycloak.models.sessions.mongo.entities.MongoClientSessionEntity",

View file

@ -19,13 +19,13 @@ public interface AdapterConstants {
String AUTH_DATA_PARAM_NAME = "org.keycloak.json.adapterConfig";
// Attribute passed in codeToToken request from adapter to Keycloak and saved in ClientSession. Contains ID of HttpSession on adapter
public static final String APPLICATION_SESSION_STATE = "application_session_state";
public static final String CLIENT_SESSION_STATE = "client_session_state";
// Attribute passed in codeToToken request from adapter to Keycloak and saved in ClientSession. Contains hostname of adapter where HttpSession is served
public static final String APPLICATION_SESSION_HOST = "application_session_host";
public static final String CLIENT_SESSION_HOST = "client_session_host";
// Attribute passed in registerNode request for register new application cluster node once he joined cluster
public static final String APPLICATION_CLUSTER_HOST = "application_cluster_host";
public static final String CLIENT_CLUSTER_HOST = "client_cluster_host";
// Cookie used on adapter side to store token info. Used only when tokenStore is 'COOKIE'
public static final String KEYCLOAK_ADAPTER_STATE_COOKIE = "KEYCLOAK_ADAPTER_STATE";

View file

@ -1,38 +0,0 @@
package org.keycloak.representations.idm;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ApplicationMappingsRepresentation {
protected String applicationId;
protected String application;
protected List<RoleRepresentation> mappings;
public String getApplicationId() {
return applicationId;
}
public void setApplicationId(String applicationId) {
this.applicationId = applicationId;
}
public String getApplication() {
return application;
}
public void setApplication(String application) {
this.application = application;
}
public List<RoleRepresentation> getMappings() {
return mappings;
}
public void setMappings(List<RoleRepresentation> mappings) {
this.mappings = mappings;
}
}

View file

@ -1,44 +1,14 @@
package org.keycloak.representations.idm;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ApplicationRepresentation {
protected String id;
@Deprecated
public class ApplicationRepresentation extends ClientRepresentation {
protected String name;
protected String adminUrl;
protected String baseUrl;
protected Boolean surrogateAuthRequired;
protected Boolean enabled;
protected String secret;
protected String[] defaultRoles;
protected List<String> redirectUris;
protected List<String> webOrigins;
@Deprecated
protected ClaimRepresentation claims;
protected Integer notBefore;
protected Boolean bearerOnly;
protected Boolean publicClient;
protected Boolean frontchannelLogout;
protected String protocol;
protected Map<String, String> attributes;
protected Boolean fullScopeAllowed;
protected Integer nodeReRegistrationTimeout;
protected Map<String, Integer> registeredNodes;
protected List<ClientIdentityProviderMappingRepresentation> identityProviders;
protected List<ProtocolMapperRepresentation> protocolMappers;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
@ -48,70 +18,6 @@ public class ApplicationRepresentation {
this.name = name;
}
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public Boolean isSurrogateAuthRequired() {
return surrogateAuthRequired;
}
public void setSurrogateAuthRequired(Boolean surrogateAuthRequired) {
this.surrogateAuthRequired = surrogateAuthRequired;
}
public String getAdminUrl() {
return adminUrl;
}
public void setAdminUrl(String adminUrl) {
this.adminUrl = adminUrl;
}
public String getBaseUrl() {
return baseUrl;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public List<String> getRedirectUris() {
return redirectUris;
}
public void setRedirectUris(List<String> redirectUris) {
this.redirectUris = redirectUris;
}
public List<String> getWebOrigins() {
return webOrigins;
}
public void setWebOrigins(List<String> webOrigins) {
this.webOrigins = webOrigins;
}
public String[] getDefaultRoles() {
return defaultRoles;
}
public void setDefaultRoles(String[] defaultRoles) {
this.defaultRoles = defaultRoles;
}
public ClaimRepresentation getClaims() {
return claims;
}
@ -119,92 +25,4 @@ public class ApplicationRepresentation {
public void setClaims(ClaimRepresentation claims) {
this.claims = claims;
}
public Integer getNotBefore() {
return notBefore;
}
public void setNotBefore(Integer notBefore) {
this.notBefore = notBefore;
}
public Boolean isBearerOnly() {
return bearerOnly;
}
public void setBearerOnly(Boolean bearerOnly) {
this.bearerOnly = bearerOnly;
}
public Boolean isPublicClient() {
return publicClient;
}
public void setPublicClient(Boolean publicClient) {
this.publicClient = publicClient;
}
public Boolean isFullScopeAllowed() {
return fullScopeAllowed;
}
public void setFullScopeAllowed(Boolean fullScopeAllowed) {
this.fullScopeAllowed = fullScopeAllowed;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public Map<String, String> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}
public Integer getNodeReRegistrationTimeout() {
return nodeReRegistrationTimeout;
}
public void setNodeReRegistrationTimeout(Integer nodeReRegistrationTimeout) {
this.nodeReRegistrationTimeout = nodeReRegistrationTimeout;
}
public Map<String, Integer> getRegisteredNodes() {
return registeredNodes;
}
public void setRegisteredNodes(Map<String, Integer> registeredNodes) {
this.registeredNodes = registeredNodes;
}
public Boolean isFrontchannelLogout() {
return frontchannelLogout;
}
public void setFrontchannelLogout(Boolean frontchannelLogout) {
this.frontchannelLogout = frontchannelLogout;
}
public List<ClientIdentityProviderMappingRepresentation> getIdentityProviders() {
return this.identityProviders;
}
public void setIdentityProviders(List<ClientIdentityProviderMappingRepresentation> identityProviders) {
this.identityProviders = identityProviders;
}
public List<ProtocolMapperRepresentation> getProtocolMappers() {
return protocolMappers;
}
public void setProtocolMappers(List<ProtocolMapperRepresentation> protocolMappers) {
this.protocolMappers = protocolMappers;
}
}

View file

@ -0,0 +1,38 @@
package org.keycloak.representations.idm;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ClientMappingsRepresentation {
protected String id;
protected String client;
protected List<RoleRepresentation> mappings;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClient() {
return client;
}
public void setClient(String client) {
this.client = client;
}
public List<RoleRepresentation> getMappings() {
return mappings;
}
public void setMappings(List<RoleRepresentation> mappings) {
this.mappings = mappings;
}
}

View file

@ -0,0 +1,218 @@
package org.keycloak.representations.idm;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ClientRepresentation {
protected String id;
protected String clientId;
protected String adminUrl;
protected String baseUrl;
protected Boolean surrogateAuthRequired;
protected Boolean enabled;
protected String secret;
protected String[] defaultRoles;
protected List<String> redirectUris;
protected List<String> webOrigins;
protected Integer notBefore;
protected Boolean bearerOnly;
protected Boolean consentRequired;
protected Boolean directGrantsOnly;
protected Boolean publicClient;
protected Boolean frontchannelLogout;
protected String protocol;
protected Map<String, String> attributes;
protected Boolean fullScopeAllowed;
protected Integer nodeReRegistrationTimeout;
protected Map<String, Integer> registeredNodes;
protected List<ClientIdentityProviderMappingRepresentation> identityProviders;
protected List<ProtocolMapperRepresentation> protocolMappers;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public Boolean isSurrogateAuthRequired() {
return surrogateAuthRequired;
}
public void setSurrogateAuthRequired(Boolean surrogateAuthRequired) {
this.surrogateAuthRequired = surrogateAuthRequired;
}
public String getAdminUrl() {
return adminUrl;
}
public void setAdminUrl(String adminUrl) {
this.adminUrl = adminUrl;
}
public String getBaseUrl() {
return baseUrl;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public List<String> getRedirectUris() {
return redirectUris;
}
public void setRedirectUris(List<String> redirectUris) {
this.redirectUris = redirectUris;
}
public List<String> getWebOrigins() {
return webOrigins;
}
public void setWebOrigins(List<String> webOrigins) {
this.webOrigins = webOrigins;
}
public String[] getDefaultRoles() {
return defaultRoles;
}
public void setDefaultRoles(String[] defaultRoles) {
this.defaultRoles = defaultRoles;
}
public Integer getNotBefore() {
return notBefore;
}
public void setNotBefore(Integer notBefore) {
this.notBefore = notBefore;
}
public Boolean isBearerOnly() {
return bearerOnly;
}
public void setBearerOnly(Boolean bearerOnly) {
this.bearerOnly = bearerOnly;
}
public Boolean isConsentRequired() {
return consentRequired;
}
public void setConsentRequired(Boolean consentRequired) {
this.consentRequired = consentRequired;
}
public Boolean getDirectGrantsOnly() {
return directGrantsOnly;
}
public void setDirectGrantsOnly(Boolean directGrantsOnly) {
this.directGrantsOnly = directGrantsOnly;
}
public Boolean isPublicClient() {
return publicClient;
}
public void setPublicClient(Boolean publicClient) {
this.publicClient = publicClient;
}
public Boolean isFullScopeAllowed() {
return fullScopeAllowed;
}
public void setFullScopeAllowed(Boolean fullScopeAllowed) {
this.fullScopeAllowed = fullScopeAllowed;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public Map<String, String> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}
public Integer getNodeReRegistrationTimeout() {
return nodeReRegistrationTimeout;
}
public void setNodeReRegistrationTimeout(Integer nodeReRegistrationTimeout) {
this.nodeReRegistrationTimeout = nodeReRegistrationTimeout;
}
public Map<String, Integer> getRegisteredNodes() {
return registeredNodes;
}
public void setRegisteredNodes(Map<String, Integer> registeredNodes) {
this.registeredNodes = registeredNodes;
}
public Boolean isFrontchannelLogout() {
return frontchannelLogout;
}
public void setFrontchannelLogout(Boolean frontchannelLogout) {
this.frontchannelLogout = frontchannelLogout;
}
public List<ClientIdentityProviderMappingRepresentation> getIdentityProviders() {
return this.identityProviders;
}
public void setIdentityProviders(List<ClientIdentityProviderMappingRepresentation> identityProviders) {
this.identityProviders = identityProviders;
}
public List<ProtocolMapperRepresentation> getProtocolMappers() {
return protocolMappers;
}
public void setProtocolMappers(List<ProtocolMapperRepresentation> protocolMappers) {
this.protocolMappers = protocolMappers;
}
}

View file

@ -9,7 +9,7 @@ import java.util.Map;
*/
public class MappingsRepresentation {
protected List<RoleRepresentation> realmMappings;
protected Map<String, ApplicationMappingsRepresentation> applicationMappings;
protected Map<String, ClientMappingsRepresentation> clientMappings;
public List<RoleRepresentation> getRealmMappings() {
return realmMappings;
@ -19,11 +19,11 @@ public class MappingsRepresentation {
this.realmMappings = realmMappings;
}
public Map<String, ApplicationMappingsRepresentation> getApplicationMappings() {
return applicationMappings;
public Map<String, ClientMappingsRepresentation> getClientMappings() {
return clientMappings;
}
public void setApplicationMappings(Map<String, ApplicationMappingsRepresentation> applicationMappings) {
this.applicationMappings = applicationMappings;
public void setClientMappings(Map<String, ClientMappingsRepresentation> clientMappings) {
this.clientMappings = clientMappings;
}
}

View file

@ -1,157 +1,9 @@
package org.keycloak.representations.idm;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OAuthClientRepresentation {
protected String id;
protected String name;
protected List<String> redirectUris;
protected List<String> webOrigins;
protected Boolean enabled;
protected String secret;
@Deprecated
protected ClaimRepresentation claims;
protected Integer notBefore;
protected Boolean publicClient;
protected String protocol;
protected Map<String, String> attributes;
protected Boolean directGrantsOnly;
protected Boolean fullScopeAllowed;
protected Boolean frontchannelLogout;
protected List<ProtocolMapperRepresentation> protocolMappers;
private List<ClientIdentityProviderMappingRepresentation> identityProviders;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public List<String> getRedirectUris() {
return redirectUris;
}
public void setRedirectUris(List<String> redirectUris) {
this.redirectUris = redirectUris;
}
public List<String> getWebOrigins() {
return webOrigins;
}
public void setWebOrigins(List<String> webOrigins) {
this.webOrigins = webOrigins;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public ClaimRepresentation getClaims() {
return claims;
}
public void setClaims(ClaimRepresentation claims) {
this.claims = claims;
}
public Integer getNotBefore() {
return notBefore;
}
public void setNotBefore(Integer notBefore) {
this.notBefore = notBefore;
}
public Boolean isPublicClient() {
return publicClient;
}
public void setPublicClient(Boolean publicClient) {
this.publicClient = publicClient;
}
public Boolean isDirectGrantsOnly() {
return directGrantsOnly;
}
public void setDirectGrantsOnly(Boolean directGrantsOnly) {
this.directGrantsOnly = directGrantsOnly;
}
public Boolean isFullScopeAllowed() {
return fullScopeAllowed;
}
public void setFullScopeAllowed(Boolean fullScopeAllowed) {
this.fullScopeAllowed = fullScopeAllowed;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public Map<String, String> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}
public Boolean isFrontchannelLogout() {
return frontchannelLogout;
}
public void setFrontchannelLogout(Boolean frontchannelLogout) {
this.frontchannelLogout = frontchannelLogout;
}
public List<ClientIdentityProviderMappingRepresentation> getIdentityProviders() {
return this.identityProviders;
}
public void setIdentityProviders(List<ClientIdentityProviderMappingRepresentation> identityProviders) {
this.identityProviders = identityProviders;
}
public List<ProtocolMapperRepresentation> getProtocolMappers() {
return protocolMappers;
}
public void setProtocolMappers(List<ProtocolMapperRepresentation> protocolMappers) {
this.protocolMappers = protocolMappers;
}
@Deprecated
public class OAuthClientRepresentation extends ApplicationRepresentation {
}

View file

@ -25,13 +25,6 @@ public class RealmRepresentation {
protected Boolean verifyEmail;
protected Boolean resetPasswordAllowed;
@Deprecated
protected Boolean social;
@Deprecated
protected Boolean updateProfileOnInitialSocialLogin;
@Deprecated
protected Map<String, String> socialProviders;
protected Boolean userCacheEnabled;
protected Boolean realmCacheEnabled;
@ -55,9 +48,8 @@ public class RealmRepresentation {
protected String passwordPolicy;
protected List<UserRepresentation> users;
protected List<ScopeMappingRepresentation> scopeMappings;
protected Map<String, List<ScopeMappingRepresentation>> applicationScopeMappings;
protected List<ApplicationRepresentation> applications;
protected List<OAuthClientRepresentation> oauthClients;
protected Map<String, List<ScopeMappingRepresentation>> clientScopeMappings;
protected List<ClientRepresentation> clients;
protected Map<String, String> browserSecurityHeaders;
protected Map<String, String> smtpServer;
protected List<UserFederationProviderRepresentation> userFederationProviders;
@ -76,6 +68,18 @@ public class RealmRepresentation {
protected Set<String> supportedLocales;
protected String defaultLocale;
@Deprecated
protected Boolean social;
@Deprecated
protected Boolean updateProfileOnInitialSocialLogin;
@Deprecated
protected Map<String, String> socialProviders;
@Deprecated
protected Map<String, List<ScopeMappingRepresentation>> applicationScopeMappings;
@Deprecated
protected List<ApplicationRepresentation> applications;
@Deprecated
protected List<OAuthClientRepresentation> oauthClients;
public String getId() {
return id;
@ -101,14 +105,6 @@ public class RealmRepresentation {
return applications;
}
public ApplicationRepresentation resource(String name) {
ApplicationRepresentation resource = new ApplicationRepresentation();
if (applications == null) applications = new ArrayList<ApplicationRepresentation>();
applications.add(resource);
resource.setName(name);
return resource;
}
public void setUsers(List<UserRepresentation> users) {
this.users = users;
}
@ -121,8 +117,12 @@ public class RealmRepresentation {
return user;
}
public void setApplications(List<ApplicationRepresentation> applications) {
this.applications = applications;
public List<ClientRepresentation> getClients() {
return clients;
}
public void setClients(List<ClientRepresentation> clients) {
this.clients = clients;
}
public Boolean isEnabled() {
@ -321,22 +321,16 @@ public class RealmRepresentation {
this.resetPasswordAllowed = resetPassword;
}
@Deprecated
public Boolean isSocial() {
return social;
}
public void setSocial(Boolean social) {
this.social = social;
}
@Deprecated
public Boolean isUpdateProfileOnInitialSocialLogin() {
return updateProfileOnInitialSocialLogin;
}
public void setUpdateProfileOnInitialSocialLogin(Boolean updateProfileOnInitialSocialLogin) {
this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin;
}
public Map<String, String> getBrowserSecurityHeaders() {
return browserSecurityHeaders;
}
@ -345,14 +339,11 @@ public class RealmRepresentation {
this.browserSecurityHeaders = browserSecurityHeaders;
}
@Deprecated
public Map<String, String> getSocialProviders() {
return socialProviders;
}
public void setSocialProviders(Map<String, String> socialProviders) {
this.socialProviders = socialProviders;
}
public Map<String, String> getSmtpServer() {
return smtpServer;
}
@ -361,22 +352,24 @@ public class RealmRepresentation {
this.smtpServer = smtpServer;
}
@Deprecated
public List<OAuthClientRepresentation> getOauthClients() {
return oauthClients;
}
public void setOauthClients(List<OAuthClientRepresentation> oauthClients) {
this.oauthClients = oauthClients;
public Map<String, List<ScopeMappingRepresentation>> getClientScopeMappings() {
return clientScopeMappings;
}
public void setClientScopeMappings(Map<String, List<ScopeMappingRepresentation>> clientScopeMappings) {
this.clientScopeMappings = clientScopeMappings;
}
@Deprecated
public Map<String, List<ScopeMappingRepresentation>> getApplicationScopeMappings() {
return applicationScopeMappings;
}
public void setApplicationScopeMappings(Map<String, List<ScopeMappingRepresentation>> applicationScopeMappings) {
this.applicationScopeMappings = applicationScopeMappings;
}
public RolesRepresentation getRoles() {
return roles;
}

View file

@ -17,9 +17,10 @@ public class RoleRepresentation {
public static class Composites {
protected Set<String> realm;
protected Map<String, List<String>> client;
@Deprecated
protected Map<String, List<String>> application;
public Set<String> getRealm() {
return realm;
}
@ -28,12 +29,17 @@ public class RoleRepresentation {
this.realm = realm;
}
public Map<String, List<String>> getApplication() {
return application;
public Map<String, List<String>> getClient() {
return client;
}
public void setApplication(Map<String, List<String>> application) {
this.application = application;
public void setClient(Map<String, List<String>> client) {
this.client = client;
}
@Deprecated
public Map<String, List<String>> getApplication() {
return application;
}
}

View file

@ -9,6 +9,8 @@ import java.util.Map;
*/
public class RolesRepresentation {
protected List<RoleRepresentation> realm;
protected Map<String, List<RoleRepresentation>> client;
@Deprecated
protected Map<String, List<RoleRepresentation>> application;
public List<RoleRepresentation> getRealm() {
@ -19,11 +21,16 @@ public class RolesRepresentation {
this.realm = realm;
}
public Map<String, List<RoleRepresentation>> getClient() {
return client;
}
public void setClient(Map<String, List<RoleRepresentation>> client) {
this.client = client;
}
@Deprecated
public Map<String, List<RoleRepresentation>> getApplication() {
return application;
}
public void setApplication(Map<String, List<RoleRepresentation>> application) {
this.application = application;
}
}

View file

@ -25,10 +25,13 @@ public class UserRepresentation {
protected List<CredentialRepresentation> credentials;
protected List<String> requiredActions;
protected List<FederatedIdentityRepresentation> federatedIdentities;
protected List<String> realmRoles;
protected Map<String, List<String>> clientRoles;
@Deprecated
protected Map<String, List<String>> applicationRoles;
@Deprecated
protected List<SocialLinkRepresentation> socialLinks;
protected List<String> realmRoles;
protected Map<String, List<String>> applicationRoles;
public String getSelf() {
return self;
@ -165,12 +168,17 @@ public class UserRepresentation {
this.realmRoles = realmRoles;
}
public Map<String, List<String>> getApplicationRoles() {
return applicationRoles;
public Map<String, List<String>> getClientRoles() {
return clientRoles;
}
public void setApplicationRoles(Map<String, List<String>> applicationRoles) {
this.applicationRoles = applicationRoles;
public void setClientRoles(Map<String, List<String>> clientRoles) {
this.clientRoles = clientRoles;
}
@Deprecated
public Map<String, List<String>> getApplicationRoles() {
return applicationRoles;
}
public String getFederationLink() {

View file

@ -15,7 +15,6 @@ public class UserSessionRepresentation {
private String ipAddress;
private long start;
private long lastAccess;
private Map<String, String> applications = new HashMap<String, String>();
private Map<String, String> clients = new HashMap<String, String>();
public String getId() {
@ -58,14 +57,6 @@ public class UserSessionRepresentation {
this.lastAccess = lastAccess;
}
public Map<String, String> getApplications() {
return applications;
}
public void setApplications(Map<String, String> applications) {
this.applications = applications;
}
public Map<String, String> getClients() {
return clients;
}

View file

@ -17,6 +17,10 @@ public class Time {
return new Date(((long) time ) * 1000);
}
public static long toMillis(int time) {
return ((long) time) * 1000;
}
public static void setOffset(int offset) {
Time.offset = offset;
}

View file

@ -144,34 +144,6 @@
<artifactId>keycloak-kerberos-federation</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-common</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-api</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-impl</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-simple-schema</artifactId>
</dependency>
<!-- picketlink -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-picketlink-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-picketlink-ldap</artifactId>
<version>${project.version}</version>
</dependency>
<!-- saml -->
<dependency>

View file

@ -259,14 +259,6 @@
<maven-resource group="org.keycloak" artifact="keycloak-ldap-federation"/>
</module-def>
<module-def name="org.keycloak.keycloak-picketlink-api">
<maven-resource group="org.keycloak" artifact="keycloak-picketlink-api"/>
</module-def>
<module-def name="org.keycloak.keycloak-picketlink-ldap">
<maven-resource group="org.keycloak" artifact="keycloak-picketlink-ldap"/>
</module-def>
<module-def name="org.keycloak.keycloak-saml-core">
<maven-resource group="org.keycloak" artifact="keycloak-saml-core"/>
</module-def>

View file

@ -10,14 +10,9 @@
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-model-api"/>
<module name="org.keycloak.keycloak-kerberos-federation"/>
<module name="org.keycloak.keycloak-picketlink-api"/>
<module name="javax.ws.rs.api"/>
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
<module name="org.jboss.logging"/>
<module name="org.picketlink.common"/>
<module name="org.picketlink.idm.api"/>
<module name="org.picketlink.idm"/>
<module name="org.picketlink.idm.schema"/>
<module name="javax.api"/>
</dependencies>

View file

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-picketlink-api">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-model-api"/>
<module name="org.keycloak.keycloak-picketlink-api"/>
<module name="org.jboss.logging"/>
<module name="org.picketlink.common"/>
<module name="org.picketlink.idm.api"/>
<module name="org.picketlink.idm"/>
<module name="org.picketlink.idm.schema"/>
<module name="javax.api"/>
</dependencies>
</module>

View file

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-picketlink-ldap">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-model-api"/>
<module name="org.keycloak.keycloak-picketlink-api"/>
<module name="org.jboss.logging"/>
<module name="org.picketlink.common"/>
<module name="org.picketlink.idm.api"/>
<module name="org.picketlink.idm"/>
<module name="org.picketlink.idm.schema"/>
<module name="javax.api"/>
</dependencies>
</module>

View file

@ -48,8 +48,6 @@
<module name="org.keycloak.keycloak-model-sessions-jpa" services="import"/>
<module name="org.keycloak.keycloak-model-sessions-mem" services="import"/>
<module name="org.keycloak.keycloak-model-sessions-mongo" services="import"/>
<module name="org.keycloak.keycloak-picketlink-api" services="import"/>
<module name="org.keycloak.keycloak-picketlink-ldap" services="import"/>
<module name="org.keycloak.keycloak-saml-protocol" services="import"/>
<module name="org.keycloak.keycloak-services" export="true" services="import"/>
<module name="org.keycloak.keycloak-social-core" services="import"/>

View file

@ -49,10 +49,8 @@
<module name="org.keycloak.keycloak-model-sessions-jpa" services="import"/>
<module name="org.keycloak.keycloak-model-sessions-mem" services="import"/>
<module name="org.keycloak.keycloak-model-sessions-mongo" services="import"/>
<module name="org.keycloak.keycloak-picketlink-api" services="import"/>
<module name="org.keycloak.keycloak-wildfly-extensions" services="import"/>
<module name="org.keycloak.keycloak-picketlink-ldap" services="import"/>
<module name="org.keycloak.keycloak-saml-protocol" services="import"/>
<module name="org.keycloak.keycloak-services" export="true" services="import"/>
<module name="org.keycloak.keycloak-social-core" services="import"/>

View file

@ -40,9 +40,6 @@
<module name="org.keycloak.keycloak-model-sessions-jpa" services="import"/>
<module name="org.keycloak.keycloak-model-sessions-mem" services="import"/>
<module name="org.keycloak.keycloak-model-sessions-mongo" services="import"/>
<module name="org.keycloak.keycloak-picketlink-api" services="import"/>
<module name="org.keycloak.keycloak-picketlink-ldap" services="import"/>
<module name="org.keycloak.keycloak-saml-protocol" services="import"/>
<module name="org.keycloak.keycloak-services" export="true" services="import"/>
<module name="org.keycloak.keycloak-social-core" services="import"/>

View file

@ -284,6 +284,15 @@
during the authentication process.
</entry>
</row>
<row>
<entry>
<literal>GUI order</literal>
</entry>
<entry>
Allows you to define order of the provider when shown on login page.
You can put number into this field, providers with lower numbers are shown first.
</entry>
</row>
<!--<row>-->
<!--<entry>-->
<!--<literal>Store Tokens</literal>-->

View file

@ -80,7 +80,7 @@
<programlisting>[<![CDATA[
"theme": {
"default": "keycloak",
"staticMaxAge": 01,
"staticMaxAge": -1,
"cacheTemplates": false,
"cacheThemes": false,
"folder": {
@ -205,7 +205,7 @@ import=common/keycloak
</para>
<para>
To deploy a theme as a module you need to create an jar (it's basically just a zip with jar extension) with
the theme resources and a file <literal>META/keycloak-server.json</literal> that describes the themes contained
the theme resources and a file <literal>META/keycloak-themes.json</literal> that describes the themes contained
in the archive. For example <literal>example-theme.jar</literal> with the contents:
<itemizedlist>
<listitem>META-INF/keycloak-themes.json</listitem>
@ -213,7 +213,7 @@ import=common/keycloak
<listitem>theme/example-theme/login/login.ftl</listitem>
<listitem>theme/example-theme/login/resources/css/styles.css</listitem>
</itemizedlist>
The contents of META-INF/keycloak-server.json in this case would be:
The contents of META-INF/keycloak-themes.json in this case would be:
<programlisting>[<![CDATA[
{
"themes": [{

View file

@ -17,12 +17,12 @@
"value": "password"
}
],
"applicationRoles": {
"clientRoles": {
"realm-management": [ "realm-admin" ]
}
}
],
"applications": [
"clients": [
{
"name": "examples-admin-client",
"enabled": true,

View file

@ -1,5 +1,5 @@
<%@ page import="org.keycloak.admin.client.Keycloak" %>
<%@ page import="org.keycloak.admin.client.resource.ApplicationsResource" %>
<%@ page import="org.keycloak.admin.client.resource.ClientsResource" %>
<%@ page import="org.keycloak.representations.idm.ApplicationRepresentation" %>
<%@ page import="org.keycloak.util.UriUtils" %>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %>
@ -13,7 +13,7 @@
String authServer = UriUtils.getOrigin(request.getRequestURL().toString()) + "/auth";
Keycloak keycloak = Keycloak.getInstance(authServer, "example", "examples-admin-client", "password", "examples-admin-client", "password");
ApplicationsResource applications = keycloak.realm("example").applications();
ClientsResource applications = keycloak.realm("example").applications();
out.println("<h1>Applications</h1>");
out.println("<ul>");

View file

@ -24,7 +24,7 @@
"value" : "password" }
],
"realmRoles": [ "user","admin" ],
"applicationRoles": {
"clientRoles": {
"realm-management": [ "realm-admin" ]
}
}
@ -41,7 +41,7 @@
}
]
},
"applications": [
"clients": [
{
"name": "basic-auth-service",
"enabled": true,

View file

@ -17,7 +17,7 @@
"value" : "password" }
],
"realmRoles": [ "user","admin" ],
"applicationRoles": {
"clientRoles": {
"realm-management": [ "realm-admin" ]
}
}
@ -30,7 +30,7 @@
}
]
},
"applications": [
"clients": [
{
"name": "facebook-authentication",
"enabled": true,

View file

@ -17,7 +17,7 @@
"value" : "password" }
],
"realmRoles": [ "user","admin" ],
"applicationRoles": {
"clientRoles": {
"realm-management": [ "realm-admin" ]
}
}
@ -30,7 +30,7 @@
}
]
},
"applications": [
"clients": [
{
"name": "google-authentication",
"enabled": true,

View file

@ -17,7 +17,7 @@
"value" : "password" }
],
"realmRoles": [ "user","admin" ],
"applicationRoles": {
"clientRoles": {
"realm-management": [ "realm-admin" ]
}
}
@ -30,7 +30,7 @@
}
]
},
"applications": [
"clients": [
{
"name": "saml-broker-authentication",
"enabled": true,

View file

@ -26,7 +26,7 @@
}
]
},
"applications": [
"clients": [
{
"name": "http://localhost:8080/auth/realms/saml-broker-authentication-realm",
"protocol": "saml",

View file

@ -18,7 +18,7 @@
"value" : "password" }
],
"realmRoles": [ "user","admin" ],
"applicationRoles": {
"clientRoles": {
"realm-management": [ "realm-admin" ]
}
}
@ -31,7 +31,7 @@
}
]
},
"applications": [
"clients": [
{
"name": "twitter-authentication",
"enabled": true,

View file

@ -18,7 +18,7 @@
"value" : "password" }
],
"realmRoles": [ "user" ],
"applicationRoles": {
"clientRoles": {
"account": ["view-profile", "manage-account"]
}
}
@ -41,7 +41,7 @@
"roles": ["user"]
}
],
"applications": [
"clients": [
{
"name": "cordova",
"enabled": true,
@ -50,7 +50,7 @@
"webOrigins": ["localhost"]
}
],
"applicationScopeMappings": {
"clientScopeMappings": {
"account": [
{
"client": "cordova",

View file

@ -23,7 +23,7 @@
"value" : "password" }
],
"realmRoles": [ "user" ],
"applicationRoles": {
"clientRoles": {
"realm-management": [ "realm-admin" ]
}
}
@ -42,7 +42,7 @@
"roles": ["user"]
}
],
"applications": [
"clients": [
{
"name": "angular-cors-product",
"enabled": true,
@ -56,7 +56,7 @@
]
}
],
"applicationScopeMappings": {
"clientScopeMappings": {
"realm-management": [
{
"client": "angular-cors-product",

View file

@ -24,7 +24,7 @@
"value" : "password" }
],
"realmRoles": [ "user" ],
"applicationRoles": {
"clientRoles": {
"account": [ "manage-account" ]
}
},
@ -39,7 +39,7 @@
"value" : "password" }
],
"realmRoles": [ "user" ],
"applicationRoles": {
"clientRoles": {
"account": [ "manage-account" ]
}
},
@ -54,7 +54,7 @@
"value" : "password" }
],
"realmRoles": [ "user" ],
"applicationRoles": {
"clientRoles": {
"account": [ "manage-account" ]
}
},
@ -69,7 +69,7 @@
"value" : "password" }
],
"realmRoles": [ "user","admin" ],
"applicationRoles": {
"clientRoles": {
"realm-management": [ "realm-admin" ]
}
}
@ -92,7 +92,7 @@
"roles": ["user"]
}
],
"applications": [
"clients": [
{
"name": "customer-portal",
"enabled": true,
@ -146,12 +146,11 @@
"adminUrl": "/database",
"baseUrl": "/database",
"bearerOnly": true
}
],
"oauthClients": [
},
{
"name": "third-party",
"enabled": true,
"consentRequired": true,
"redirectUris": [
"/oauth-client/*",
"/oauth-client-cdi/*"
@ -162,11 +161,11 @@
"name": "admin-client",
"enabled": true,
"publicClient": true,
"directGrantsOnly": true
"directGrantsOnly": true,
"consentRequired": true
}
],
"applicationScopeMappings": {
"clientScopeMappings": {
"realm-management": [
{
"client": "admin-client",

View file

@ -24,7 +24,7 @@
"value" : "password" }
],
"realmRoles": [ "user" ],
"applicationRoles": {
"clientRoles": {
"account": [ "manage-account" ]
}
},
@ -39,7 +39,7 @@
"value" : "password" }
],
"realmRoles": [ "user" ],
"applicationRoles": {
"clientRoles": {
"account": [ "manage-account" ]
}
},
@ -54,7 +54,7 @@
"value" : "password" }
],
"realmRoles": [ "user" ],
"applicationRoles": {
"clientRoles": {
"account": [ "manage-account" ]
}
},
@ -69,7 +69,7 @@
"value" : "password" }
],
"realmRoles": [ "user","admin" ],
"applicationRoles": {
"clientRoles": {
"realm-management": [ "realm-admin" ]
}
},
@ -84,7 +84,7 @@
"value" : "password" }
],
"realmRoles": [ "user", "jmxAdmin" ],
"applicationRoles": {
"clientRoles": {
"account": [ "manage-account" ],
"realm-management": [ "realm-admin" ]
}
@ -134,7 +134,7 @@
}
]
},
"applications": [
"clients": [
{
"name": "customer-portal",
"enabled": true,
@ -178,14 +178,13 @@
"adminUrl": "http://localhost:8383/admin-camel-endpoint",
"baseUrl": "http://localhost:8383/admin-camel-endpoint",
"bearerOnly": true
}
],
"oauthClients": [
},
{
"name": "ssh-jmx-admin-client",
"enabled": true,
"publicClient": false,
"directGrantsOnly": true,
"consentRequired": true,
"secret": "password"
}
],

View file

@ -18,7 +18,7 @@
"value" : "password" }
],
"realmRoles": [ "user" ],
"applicationRoles": {
"clientRoles": {
"account": ["view-profile", "manage-account"]
}
}
@ -41,7 +41,7 @@
"roles": ["user"]
}
],
"applications": [
"clients": [
{
"name": "js-console",
"enabled": true,
@ -53,7 +53,7 @@
"webOrigins": []
}
],
"applicationScopeMappings": {
"clientScopeMappings": {
"account": [
{
"client": "js-console",

View file

@ -13,7 +13,7 @@
"roles": [ "user" ]
}
],
"applications": [
"clients": [
{
"name": "kerberos-app",
"enabled": true,

View file

@ -20,7 +20,7 @@
"value" : "user-tenant1" }
],
"realmRoles": [ "user" ],
"applicationRoles": {
"clientRoles": {
"multi-tenant": [ "user" ]
}
}
@ -40,7 +40,7 @@
}
],
"applications": [
"clients": [
{
"name": "multi-tenant",
"enabled": true,

View file

@ -21,7 +21,7 @@
"value" : "user-tenant2" }
],
"realmRoles": [ "user" ],
"applicationRoles": {
"clientRoles": {
"multi-tenant": [ "user" ]
}
}
@ -41,9 +41,9 @@
}
],
"applications": [
"clients": [
{
"name": "multi-tenant",
"clientId": "multi-tenant",
"enabled": true,
"adminUrl": "/multitenant/tenant2",
"baseUrl": "/multitenant/tenant2",

View file

@ -25,9 +25,9 @@
"realmRoles": ["manager"]
}
],
"applications": [
"clients": [
{
"name": "http://localhost:8080/sales-post/",
"clientId": "http://localhost:8080/sales-post/",
"enabled": true,
"fullScopeAllowed": true,
"protocol": "saml",
@ -41,7 +41,7 @@
}
},
{
"name": "http://localhost:8080/sales-post-sig/",
"clientId": "http://localhost:8080/sales-post-sig/",
"enabled": true,
"protocol": "saml",
"fullScopeAllowed": true,
@ -60,7 +60,7 @@
}
},
{
"name": "http://localhost:8080/sales-post-enc/",
"clientId": "http://localhost:8080/sales-post-enc/",
"enabled": true,
"protocol": "saml",
"fullScopeAllowed": true,
@ -82,7 +82,7 @@
}
},
{
"name": "http://localhost:8080/employee/",
"clientId": "http://localhost:8080/employee/",
"enabled": true,
"fullScopeAllowed": true,
"protocol": "saml",
@ -96,7 +96,7 @@
}
},
{
"name": "http://localhost:8080/employee-sig/",
"clientId": "http://localhost:8080/employee-sig/",
"enabled": true,
"protocol": "saml",
"fullScopeAllowed": true,

View file

@ -6,10 +6,8 @@ import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
@ -18,9 +16,8 @@ import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.ClaimRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.OAuthClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.RolesRepresentation;
@ -57,52 +54,41 @@ public class ExportUtils {
rep.setEventsListeners(new LinkedList<String>(realm.getEventsListeners()));
}
// Applications
List<ApplicationModel> applications = realm.getApplications();
List<ApplicationRepresentation> appReps = new ArrayList<ApplicationRepresentation>();
for (ApplicationModel app : applications) {
ApplicationRepresentation appRep = exportApplication(app);
appReps.add(appRep);
// Clients
List<ClientModel> clients = realm.getClients();
List<ClientRepresentation> clientReps = new ArrayList<>();
for (ClientModel app : clients) {
ClientRepresentation clientRep = exportClient(app);
clientReps.add(clientRep);
}
rep.setApplications(appReps);
// OAuth clients
List<OAuthClientModel> oauthClients = realm.getOAuthClients();
List<OAuthClientRepresentation> oauthClientReps = new ArrayList<OAuthClientRepresentation>();
for (OAuthClientModel oauthClient : oauthClients) {
OAuthClientRepresentation clientRep = ModelToRepresentation.toRepresentation(oauthClient);
clientRep.setSecret(oauthClient.getSecret());
oauthClientReps.add(clientRep);
}
rep.setOauthClients(oauthClientReps);
rep.setClients(clientReps);
// Roles
List<RoleRepresentation> realmRoleReps = null;
Map<String, List<RoleRepresentation>> appRolesReps = new HashMap<String, List<RoleRepresentation>>();
Map<String, List<RoleRepresentation>> clientRolesReps = new HashMap<>();
Set<RoleModel> realmRoles = realm.getRoles();
if (realmRoles != null && realmRoles.size() > 0) {
realmRoleReps = exportRoles(realmRoles);
}
for (ApplicationModel app : applications) {
Set<RoleModel> currentAppRoles = app.getRoles();
for (ClientModel client : clients) {
Set<RoleModel> currentAppRoles = client.getRoles();
List<RoleRepresentation> currentAppRoleReps = exportRoles(currentAppRoles);
appRolesReps.put(app.getName(), currentAppRoleReps);
clientRolesReps.put(client.getClientId(), currentAppRoleReps);
}
RolesRepresentation rolesRep = new RolesRepresentation();
if (realmRoleReps != null) {
rolesRep.setRealm(realmRoleReps);
}
if (appRolesReps.size() > 0) {
rolesRep.setApplication(appRolesReps);
if (clientRolesReps.size() > 0) {
rolesRep.setClient(clientRolesReps);
}
rep.setRoles(rolesRep);
// Scopes
List<ClientModel> allClients = new ArrayList<ClientModel>(applications);
allClients.addAll(realm.getOAuthClients());
Map<String, List<ScopeMappingRepresentation>> appScopeReps = new HashMap<String, List<ScopeMappingRepresentation>>();
List<ClientModel> allClients = new ArrayList<>(clients);
Map<String, List<ScopeMappingRepresentation>> clientScopeReps = new HashMap<>();
for (ClientModel client : allClients) {
Set<RoleModel> clientScopes = client.getScopeMappings();
@ -114,12 +100,12 @@ public class ExportUtils {
}
scopeMappingRep.role(scope.getName());
} else {
ApplicationModel app = (ApplicationModel)scope.getContainer();
String appName = app.getName();
List<ScopeMappingRepresentation> currentAppScopes = appScopeReps.get(appName);
ClientModel app = (ClientModel)scope.getContainer();
String appName = app.getClientId();
List<ScopeMappingRepresentation> currentAppScopes = clientScopeReps.get(appName);
if (currentAppScopes == null) {
currentAppScopes = new ArrayList<ScopeMappingRepresentation>();
appScopeReps.put(appName, currentAppScopes);
currentAppScopes = new ArrayList<>();
clientScopeReps.put(appName, currentAppScopes);
}
ScopeMappingRepresentation currentClientScope = null;
@ -139,8 +125,8 @@ public class ExportUtils {
}
}
if (appScopeReps.size() > 0) {
rep.setApplicationScopeMappings(appScopeReps);
if (clientScopeReps.size() > 0) {
rep.setClientScopeMappings(clientScopeReps);
}
// Finally users if needed
@ -162,14 +148,13 @@ public class ExportUtils {
/**
* Full export of application including claims and secret
* @param app
* @param client
* @return full ApplicationRepresentation
*/
public static ApplicationRepresentation exportApplication(ApplicationModel app) {
ApplicationRepresentation appRep = ModelToRepresentation.toRepresentation(app);
appRep.setSecret(app.getSecret());
return appRep;
public static ClientRepresentation exportClient(ClientModel client) {
ClientRepresentation clientRep = ModelToRepresentation.toRepresentation(client);
clientRep.setSecret(client.getSecret());
return clientRep;
}
public static List<RoleRepresentation> exportRoles(Collection<RoleModel> roles) {
@ -201,27 +186,27 @@ public class ExportUtils {
Set<RoleModel> composites = role.getComposites();
if (composites != null && composites.size() > 0) {
Set<String> compositeRealmRoles = null;
Map<String, List<String>> compositeAppRoles = null;
Map<String, List<String>> compositeClientRoles = null;
for (RoleModel composite : composites) {
RoleContainerModel crContainer = composite.getContainer();
if (crContainer instanceof RealmModel) {
if (compositeRealmRoles == null) {
compositeRealmRoles = new HashSet<String>();
compositeRealmRoles = new HashSet<>();
}
compositeRealmRoles.add(composite.getName());
} else {
if (compositeAppRoles == null) {
compositeAppRoles = new HashMap<String, List<String>>();
if (compositeClientRoles == null) {
compositeClientRoles = new HashMap<>();
}
ApplicationModel app = (ApplicationModel)crContainer;
String appName = app.getName();
List<String> currentAppComposites = compositeAppRoles.get(appName);
ClientModel app = (ClientModel)crContainer;
String appName = app.getClientId();
List<String> currentAppComposites = compositeClientRoles.get(appName);
if (currentAppComposites == null) {
currentAppComposites = new ArrayList<String>();
compositeAppRoles.put(appName, currentAppComposites);
currentAppComposites = new ArrayList<>();
compositeClientRoles.put(appName, currentAppComposites);
}
currentAppComposites.add(composite.getName());
}
@ -231,8 +216,8 @@ public class ExportUtils {
if (compositeRealmRoles != null) {
compRep.setRealm(compositeRealmRoles);
}
if (compositeAppRoles != null) {
compRep.setApplication(compositeAppRoles);
if (compositeClientRoles != null) {
compRep.setClient(compositeClientRoles);
}
roleRep.setComposites(compRep);
@ -263,29 +248,29 @@ public class ExportUtils {
// Role mappings
Set<RoleModel> roles = user.getRoleMappings();
List<String> realmRoleNames = new ArrayList<String>();
Map<String, List<String>> appRoleNames = new HashMap<String, List<String>>();
List<String> realmRoleNames = new ArrayList<>();
Map<String, List<String>> clientRoleNames = new HashMap<>();
for (RoleModel role : roles) {
if (role.getContainer() instanceof RealmModel) {
realmRoleNames.add(role.getName());
} else {
ApplicationModel app = (ApplicationModel)role.getContainer();
String appName = app.getName();
List<String> currentAppRoles = appRoleNames.get(appName);
if (currentAppRoles == null) {
currentAppRoles = new ArrayList<String>();
appRoleNames.put(appName, currentAppRoles);
ClientModel client = (ClientModel)role.getContainer();
String clientId = client.getClientId();
List<String> currentClientRoles = clientRoleNames.get(clientId);
if (currentClientRoles == null) {
currentClientRoles = new ArrayList<>();
clientRoleNames.put(clientId, currentClientRoles);
}
currentAppRoles.add(role.getName());
currentClientRoles.add(role.getName());
}
}
if (realmRoleNames.size() > 0) {
userRep.setRealmRoles(realmRoleNames);
}
if (appRoleNames.size() > 0) {
userRep.setApplicationRoles(appRoleNames);
if (clientRoleNames.size() > 0) {
userRep.setClientRoles(clientRoleNames);
}
// Credentials

View file

@ -8,7 +8,7 @@ import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.exportimport.Strategy;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
@ -54,7 +54,7 @@ public class ImportUtils {
if (Config.getAdminRealm().equals(realm.getId())) {
// Delete all masterAdmin apps due to foreign key constraints
for (RealmModel currRealm : model.getRealms()) {
currRealm.setMasterAdminApp(null);
currRealm.setMasterAdminClient(null);
}
}
// TODO: For migration between versions, it should be possible to delete just realm but keep it's users
@ -81,9 +81,9 @@ public class ImportUtils {
// We just imported master realm. All 'masterAdminApps' need to be refreshed
RealmModel adminRealm = realm;
for (RealmModel currentRealm : model.getRealms()) {
ApplicationModel masterApp = adminRealm.getApplicationByName(KeycloakModelUtils.getMasterRealmAdminApplicationName(currentRealm));
ClientModel masterApp = adminRealm.getClientByClientId(KeycloakModelUtils.getMasterRealmAdminApplicationClientId(currentRealm));
if (masterApp != null) {
currentRealm.setMasterAdminApp(masterApp);
currentRealm.setMasterAdminClient(masterApp);
} else {
setupMasterAdminManagement(model, currentRealm);
}
@ -91,9 +91,9 @@ public class ImportUtils {
} else {
// Need to refresh masterApp for current realm
RealmModel adminRealm = model.getRealm(adminRealmId);
ApplicationModel masterApp = adminRealm.getApplicationByName(KeycloakModelUtils.getMasterRealmAdminApplicationName(realm));
ClientModel masterApp = adminRealm.getClientByClientId(KeycloakModelUtils.getMasterRealmAdminApplicationClientId(realm));
if (masterApp != null) {
realm.setMasterAdminApp(masterApp);
realm.setMasterAdminClient(masterApp);
} else {
setupMasterAdminManagement(model, realm);
}
@ -119,9 +119,9 @@ public class ImportUtils {
}
adminRole.setDescription("${role_"+AdminRoles.ADMIN+"}");
ApplicationModel realmAdminApp = KeycloakModelUtils.createApplication(adminRealm, KeycloakModelUtils.getMasterRealmAdminApplicationName(realm));
ClientModel realmAdminApp = KeycloakModelUtils.createClient(adminRealm, KeycloakModelUtils.getMasterRealmAdminApplicationClientId(realm));
realmAdminApp.setBearerOnly(true);
realm.setMasterAdminApp(realmAdminApp);
realm.setMasterAdminClient(realmAdminApp);
for (String r : AdminRoles.ALL_REALM_ROLES) {
RoleModel role = realmAdminApp.addRole(r);
@ -220,7 +220,7 @@ public class ImportUtils {
private static void importUsers(KeycloakSession session, RealmProvider model, String realmName, List<UserRepresentation> userReps) {
RealmModel realm = model.getRealmByName(realmName);
Map<String, ApplicationModel> apps = realm.getApplicationNameMap();
Map<String, ClientModel> apps = realm.getClientNameMap();
for (UserRepresentation user : userReps) {
RepresentationToModel.createUser(session, realm, user, apps);
}

View file

@ -31,12 +31,6 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-picketlink-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
@ -61,27 +55,6 @@
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-common</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-impl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-simple-schema</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View file

@ -3,6 +3,10 @@ package org.keycloak.federation.ldap;
import org.jboss.logging.Logger;
import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator;
import org.keycloak.federation.kerberos.impl.SPNEGOAuthenticator;
import org.keycloak.federation.ldap.idm.model.LDAPUser;
import org.keycloak.federation.ldap.idm.query.IdentityQuery;
import org.keycloak.federation.ldap.idm.query.IdentityQueryBuilder;
import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore;
import org.keycloak.federation.ldap.kerberos.LDAPProviderKerberosConfig;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.KeycloakSession;
@ -16,12 +20,6 @@ import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.constants.KerberosConstants;
import org.picketlink.idm.IdentityManagementException;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.model.basic.BasicModel;
import org.picketlink.idm.model.basic.User;
import org.picketlink.idm.query.IdentityQuery;
import java.util.Arrays;
import java.util.HashMap;
@ -38,23 +36,21 @@ import java.util.Set;
*/
public class LDAPFederationProvider implements UserFederationProvider {
private static final Logger logger = Logger.getLogger(LDAPFederationProvider.class);
public static final String LDAP_ID = "LDAP_ID";
public static final String SYNC_REGISTRATIONS = "syncRegistrations";
protected LDAPFederationProviderFactory factory;
protected KeycloakSession session;
protected UserFederationProviderModel model;
protected PartitionManager partitionManager;
protected LDAPIdentityStore ldapIdentityStore;
protected EditMode editMode;
protected LDAPProviderKerberosConfig kerberosConfig;
protected final Set<String> supportedCredentialTypes = new HashSet<String>();
public LDAPFederationProvider(LDAPFederationProviderFactory factory, KeycloakSession session, UserFederationProviderModel model, PartitionManager partitionManager) {
public LDAPFederationProvider(LDAPFederationProviderFactory factory, KeycloakSession session, UserFederationProviderModel model, LDAPIdentityStore ldapIdentityStore) {
this.factory = factory;
this.session = session;
this.model = model;
this.partitionManager = partitionManager;
this.ldapIdentityStore = ldapIdentityStore;
this.kerberosConfig = new LDAPProviderKerberosConfig(model);
String editModeString = model.getConfig().get(LDAPConstants.EDIT_MODE);
if (editModeString == null) {
@ -69,16 +65,6 @@ public class LDAPFederationProvider implements UserFederationProvider {
}
}
private ModelException convertIDMException(IdentityManagementException ie) {
Throwable realCause = ie;
while (realCause.getCause() != null) {
realCause = realCause.getCause();
}
// Use the message from the realCause
return new ModelException(realCause.getMessage(), ie);
}
public KeycloakSession getSession() {
return session;
}
@ -87,8 +73,8 @@ public class LDAPFederationProvider implements UserFederationProvider {
return model;
}
public PartitionManager getPartitionManager() {
return partitionManager;
public LDAPIdentityStore getLdapIdentityStore() {
return this.ldapIdentityStore;
}
@Override
@ -125,22 +111,18 @@ public class LDAPFederationProvider implements UserFederationProvider {
@Override
public boolean synchronizeRegistrations() {
return "true".equalsIgnoreCase(model.getConfig().get(SYNC_REGISTRATIONS)) && editMode == EditMode.WRITABLE;
return "true".equalsIgnoreCase(model.getConfig().get(LDAPConstants.SYNC_REGISTRATIONS)) && editMode == EditMode.WRITABLE;
}
@Override
public UserModel register(RealmModel realm, UserModel user) {
if (editMode == EditMode.READ_ONLY || editMode == EditMode.UNSYNCED) throw new IllegalStateException("Registration is not supported by this ldap server");;
if (editMode == EditMode.READ_ONLY || editMode == EditMode.UNSYNCED) throw new IllegalStateException("Registration is not supported by this ldap server");
if (!synchronizeRegistrations()) throw new IllegalStateException("Registration is not supported by this ldap server");
try {
User picketlinkUser = LDAPUtils.addUser(this.partitionManager, user.getUsername(), user.getFirstName(), user.getLastName(), user.getEmail());
user.setAttribute(LDAP_ID, picketlinkUser.getId());
return proxy(user);
} catch (IdentityManagementException ie) {
throw convertIDMException(ie);
}
LDAPUser ldapUser = LDAPUtils.addUser(this.ldapIdentityStore, user.getUsername(), user.getFirstName(), user.getLastName(), user.getEmail());
user.setAttribute(LDAPConstants.LDAP_ID, ldapUser.getId());
user.setAttribute(LDAPConstants.LDAP_ENTRY_DN, ldapUser.getEntryDN());
return proxy(user);
}
@Override
@ -150,58 +132,53 @@ public class LDAPFederationProvider implements UserFederationProvider {
return false;
}
try {
return LDAPUtils.removeUser(partitionManager, user.getUsername());
} catch (IdentityManagementException ie) {
throw convertIDMException(ie);
}
return LDAPUtils.removeUser(this.ldapIdentityStore, user.getUsername());
}
@Override
public List<UserModel> searchByAttributes(Map<String, String> attributes, RealmModel realm, int maxResults) {
List<UserModel> searchResults =new LinkedList<UserModel>();
try {
Map<String, User> plUsers = searchPicketlink(attributes, maxResults);
for (User user : plUsers.values()) {
if (session.userStorage().getUserByUsername(user.getLoginName(), realm) == null) {
UserModel imported = importUserFromPicketlink(realm, user);
searchResults.add(imported);
}
Map<String, LDAPUser> ldapUsers = searchLDAP(attributes, maxResults);
for (LDAPUser ldapUser : ldapUsers.values()) {
if (session.userStorage().getUserByUsername(ldapUser.getLoginName(), realm) == null) {
UserModel imported = importUserFromLDAP(realm, ldapUser);
searchResults.add(imported);
}
} catch (IdentityManagementException ie) {
throw convertIDMException(ie);
}
return searchResults;
}
protected Map<String, User> searchPicketlink(Map<String, String> attributes, int maxResults) {
IdentityManager identityManager = getIdentityManager();
Map<String, User> results = new HashMap<String, User>();
protected Map<String, LDAPUser> searchLDAP(Map<String, String> attributes, int maxResults) {
Map<String, LDAPUser> results = new HashMap<String, LDAPUser>();
if (attributes.containsKey(USERNAME)) {
User user = BasicModel.getUser(identityManager, attributes.get(USERNAME));
LDAPUser user = LDAPUtils.getUser(this.ldapIdentityStore, attributes.get(USERNAME));
if (user != null) {
results.put(user.getLoginName(), user);
}
}
if (attributes.containsKey(EMAIL)) {
User user = queryByEmail(identityManager, attributes.get(EMAIL));
LDAPUser user = queryByEmail(attributes.get(EMAIL));
if (user != null) {
results.put(user.getLoginName(), user);
}
}
if (attributes.containsKey(FIRST_NAME) || attributes.containsKey(LAST_NAME)) {
IdentityQuery<User> query = identityManager.createIdentityQuery(User.class);
IdentityQueryBuilder queryBuilder = this.ldapIdentityStore.createQueryBuilder();
IdentityQuery<LDAPUser> query = queryBuilder.createIdentityQuery(LDAPUser.class);
if (attributes.containsKey(FIRST_NAME)) {
query.setParameter(User.FIRST_NAME, attributes.get(FIRST_NAME));
query.where(queryBuilder.equal(LDAPUser.FIRST_NAME, attributes.get(FIRST_NAME)));
}
if (attributes.containsKey(LAST_NAME)) {
query.setParameter(User.LAST_NAME, attributes.get(LAST_NAME));
query.where(queryBuilder.equal(LDAPUser.LAST_NAME, attributes.get(LAST_NAME)));
}
query.setLimit(maxResults);
List<User> agents = query.getResultList();
for (User user : agents) {
List<LDAPUser> users = query.getResultList();
for (LDAPUser user : users) {
results.put(user.getLoginName(), user);
}
}
@ -211,85 +188,69 @@ public class LDAPFederationProvider implements UserFederationProvider {
@Override
public boolean isValid(UserModel local) {
try {
User picketlinkUser = LDAPUtils.getUser(partitionManager, local.getUsername());
if (picketlinkUser == null) {
return false;
}
return picketlinkUser.getId().equals(local.getAttribute(LDAP_ID));
} catch (IdentityManagementException ie) {
throw convertIDMException(ie);
LDAPUser ldapUser = LDAPUtils.getUser(this.ldapIdentityStore, local.getUsername());
if (ldapUser == null) {
return false;
}
return ldapUser.getId().equals(local.getAttribute(LDAPConstants.LDAP_ID));
}
@Override
public UserModel getUserByUsername(RealmModel realm, String username) {
try {
User picketlinkUser = LDAPUtils.getUser(partitionManager, username);
if (picketlinkUser == null) {
return null;
}
// KEYCLOAK-808: Should we allow case-sensitivity to be configurable?
if (!username.equals(picketlinkUser.getLoginName())) {
logger.warnf("User found in LDAP but with different username. LDAP username: %s, Searched username: %s", username, picketlinkUser.getLoginName());
return null;
}
return importUserFromPicketlink(realm, picketlinkUser);
} catch (IdentityManagementException ie) {
throw convertIDMException(ie);
}
}
public IdentityManager getIdentityManager() {
return partitionManager.createIdentityManager();
}
protected UserModel importUserFromPicketlink(RealmModel realm, User picketlinkUser) {
String email = (picketlinkUser.getEmail() != null && picketlinkUser.getEmail().trim().length() > 0) ? picketlinkUser.getEmail() : null;
if (picketlinkUser.getLoginName() == null) {
throw new ModelException("User returned from LDAP has null username! Check configuration of your LDAP mappings. ID of user from LDAP: " + picketlinkUser.getId());
LDAPUser ldapUser = LDAPUtils.getUser(this.ldapIdentityStore, username);
if (ldapUser == null) {
return null;
}
UserModel imported = session.userStorage().addUser(realm, picketlinkUser.getLoginName());
// KEYCLOAK-808: Should we allow case-sensitivity to be configurable?
if (!username.equals(ldapUser.getLoginName())) {
logger.warnf("User found in LDAP but with different username. LDAP username: %s, Searched username: %s", username, ldapUser.getLoginName());
return null;
}
return importUserFromLDAP(realm, ldapUser);
}
protected UserModel importUserFromLDAP(RealmModel realm, LDAPUser ldapUser) {
String email = (ldapUser.getEmail() != null && ldapUser.getEmail().trim().length() > 0) ? ldapUser.getEmail() : null;
if (ldapUser.getLoginName() == null) {
throw new ModelException("User returned from LDAP has null username! Check configuration of your LDAP mappings. ID of user from LDAP: " + ldapUser.getId());
}
UserModel imported = session.userStorage().addUser(realm, ldapUser.getLoginName());
imported.setEnabled(true);
imported.setEmail(email);
imported.setFirstName(picketlinkUser.getFirstName());
imported.setLastName(picketlinkUser.getLastName());
imported.setFirstName(ldapUser.getFirstName());
imported.setLastName(ldapUser.getLastName());
imported.setFederationLink(model.getId());
imported.setAttribute(LDAP_ID, picketlinkUser.getId());
imported.setAttribute(LDAPConstants.LDAP_ID, ldapUser.getId());
imported.setAttribute(LDAPConstants.LDAP_ENTRY_DN, ldapUser.getEntryDN());
logger.debugf("Added new user from LDAP. Username: " + imported.getUsername() + ", Email: ", imported.getEmail() + ", LDAP_ID: " + picketlinkUser.getId());
logger.debugf("Imported new user from LDAP to Keycloak DB. Username: [%s], Email: [%s], LDAP_ID: [%s], LDAP Entry DN: [%s]", imported.getUsername(), imported.getEmail(),
ldapUser.getId(), ldapUser.getEntryDN());
return proxy(imported);
}
protected User queryByEmail(IdentityManager identityManager, String email) throws IdentityManagementException {
return LDAPUtils.getUserByEmail(identityManager, email);
protected LDAPUser queryByEmail(String email) {
return LDAPUtils.getUserByEmail(this.ldapIdentityStore, email);
}
@Override
public UserModel getUserByEmail(RealmModel realm, String email) {
IdentityManager identityManager = getIdentityManager();
try {
User picketlinkUser = queryByEmail(identityManager, email);
if (picketlinkUser == null) {
return null;
}
// KEYCLOAK-808: Should we allow case-sensitivity to be configurable?
if (!email.equals(picketlinkUser.getEmail())) {
logger.warnf("User found in LDAP but with different email. LDAP email: %s, Searched email: %s", email, picketlinkUser.getEmail());
return null;
}
return importUserFromPicketlink(realm, picketlinkUser);
} catch (IdentityManagementException ie) {
throw convertIDMException(ie);
LDAPUser ldapUser = queryByEmail(email);
if (ldapUser == null) {
return null;
}
// KEYCLOAK-808: Should we allow case-sensitivity to be configurable?
if (!email.equals(ldapUser.getEmail())) {
logger.warnf("User found in LDAP but with different email. LDAP email: %s, Searched email: %s", email, ldapUser.getEmail());
return null;
}
return importUserFromLDAP(realm, ldapUser);
}
@Override
@ -302,18 +263,14 @@ public class LDAPFederationProvider implements UserFederationProvider {
// complete I don't think we have to do anything here
}
public boolean validPassword(String username, String password) {
public boolean validPassword(UserModel user, String password) {
if (kerberosConfig.isAllowKerberosAuthentication() && kerberosConfig.isUseKerberosForPasswordAuthentication()) {
// Use Kerberos JAAS (Krb5LoginModule)
KerberosUsernamePasswordAuthenticator authenticator = factory.createKerberosUsernamePasswordAuthenticator(kerberosConfig);
return authenticator.validUser(username, password);
return authenticator.validUser(user.getUsername(), password);
} else {
// Use Naming LDAP API
try {
return LDAPUtils.validatePassword(partitionManager, username, password);
} catch (IdentityManagementException ie) {
throw convertIDMException(ie);
}
return LDAPUtils.validatePassword(this.ldapIdentityStore, user, password);
}
}
@ -322,7 +279,7 @@ public class LDAPFederationProvider implements UserFederationProvider {
public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
for (UserCredentialModel cred : input) {
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
return validPassword(user.getUsername(), cred.getValue());
return validPassword(user, cred.getValue());
} else {
return false; // invalid cred type
}
@ -353,7 +310,7 @@ public class LDAPFederationProvider implements UserFederationProvider {
UserModel user = findOrCreateAuthenticatedUser(realm, username);
if (user == null) {
logger.warn("Kerberos/SPNEGO authentication succeeded with username [" + username + "], but couldn't find or create user with federation provider [" + model.getDisplayName() + "]");
logger.warnf("Kerberos/SPNEGO authentication succeeded with username [%s], but couldn't find or create user with federation provider [%s]", username, model.getDisplayName());
return CredentialValidationOutput.failed();
} else {
String delegationCredential = spnegoAuthenticator.getSerializedDelegationCredential();
@ -375,24 +332,23 @@ public class LDAPFederationProvider implements UserFederationProvider {
@Override
public void close() {
//To change body of implemented methods use File | Settings | File Templates.
}
protected void importPicketlinkUsers(RealmModel realm, List<User> users, UserFederationProviderModel fedModel) {
for (User picketlinkUser : users) {
String username = picketlinkUser.getLoginName();
protected void importLDAPUsers(RealmModel realm, List<LDAPUser> ldapUsers, UserFederationProviderModel fedModel) {
for (LDAPUser ldapUser : ldapUsers) {
String username = ldapUser.getLoginName();
UserModel currentUser = session.userStorage().getUserByUsername(username, realm);
if (currentUser == null) {
// Add new user to Keycloak
importUserFromPicketlink(realm, picketlinkUser);
importUserFromLDAP(realm, ldapUser);
} else {
if ((fedModel.getId().equals(currentUser.getFederationLink())) && (picketlinkUser.getId().equals(currentUser.getAttribute(LDAPFederationProvider.LDAP_ID)))) {
if ((fedModel.getId().equals(currentUser.getFederationLink())) && (ldapUser.getId().equals(currentUser.getAttribute(LDAPConstants.LDAP_ID)))) {
// Update keycloak user
String email = (picketlinkUser.getEmail() != null && picketlinkUser.getEmail().trim().length() > 0) ? picketlinkUser.getEmail() : null;
String email = (ldapUser.getEmail() != null && ldapUser.getEmail().trim().length() > 0) ? ldapUser.getEmail() : null;
currentUser.setEmail(email);
currentUser.setFirstName(picketlinkUser.getFirstName());
currentUser.setLastName(picketlinkUser.getLastName());
currentUser.setFirstName(ldapUser.getFirstName());
currentUser.setLastName(ldapUser.getLastName());
logger.debugf("Updated user from LDAP: %s", currentUser.getUsername());
} else {
logger.warnf("User '%s' is not updated during sync as he is not linked to federation provider '%s'", username, fedModel.getDisplayName());
@ -404,29 +360,29 @@ public class LDAPFederationProvider implements UserFederationProvider {
/**
* Called after successful kerberos authentication
*
* @param realm
* @param realm realm
* @param username username without realm prefix
* @return
* @return finded or newly created user
*/
protected UserModel findOrCreateAuthenticatedUser(RealmModel realm, String username) {
UserModel user = session.userStorage().getUserByUsername(username, realm);
if (user != null) {
logger.debug("Kerberos authenticated user " + username + " found in Keycloak storage");
logger.debugf("Kerberos authenticated user [%s] found in Keycloak storage", username);
if (!model.getId().equals(user.getFederationLink())) {
logger.warn("User with username " + username + " already exists, but is not linked to provider [" + model.getDisplayName() + "]");
logger.warnf("User with username [%s] already exists, but is not linked to provider [%s]", username, model.getDisplayName());
return null;
} else if (isValid(user)) {
return proxy(user);
} else {
logger.warn("User with username " + username + " already exists and is linked to provider [" + model.getDisplayName() +
"] but is not valid. Stale LDAP_ID on local user is: " + user.getAttribute(LDAP_ID));
logger.warnf("User with username [%s] aready exists and is linked to provider [%s] but is not valid. Stale LDAP_ID on local user is: %s",
username, model.getDisplayName(), user.getAttribute(LDAPConstants.LDAP_ID));
logger.warn("Will re-create user");
session.userStorage().removeUser(realm, user);
}
}
// Creating user to local storage
logger.debug("Kerberos authenticated user " + username + " not in Keycloak storage. Creating him");
logger.debugf("Kerberos authenticated user [%s] not in Keycloak storage. Creating him", username);
return getUserByUsername(realm, username);
}
}

View file

@ -3,10 +3,15 @@ package org.keycloak.federation.ldap;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.federation.kerberos.CommonKerberosConfig;
import org.keycloak.federation.kerberos.KerberosConfig;
import org.keycloak.federation.kerberos.impl.KerberosServerSubjectAuthenticator;
import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator;
import org.keycloak.federation.kerberos.impl.SPNEGOAuthenticator;
import org.keycloak.federation.ldap.idm.model.IdentityType;
import org.keycloak.federation.ldap.idm.model.LDAPUser;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.IdentityQuery;
import org.keycloak.federation.ldap.idm.query.IdentityQueryBuilder;
import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakSessionTask;
@ -16,16 +21,6 @@ import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderFactory;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.picketlink.PartitionManagerProvider;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.model.IdentityType;
import org.picketlink.idm.model.basic.User;
import org.picketlink.idm.query.AttributeParameter;
import org.picketlink.idm.query.Condition;
import org.picketlink.idm.query.IdentityQuery;
import org.picketlink.idm.query.IdentityQueryBuilder;
import org.picketlink.idm.query.QueryParameter;
import java.util.Collections;
import java.util.Date;
@ -41,6 +36,8 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact
private static final Logger logger = Logger.getLogger(LDAPFederationProviderFactory.class);
public static final String PROVIDER_NAME = "ldap";
private LDAPIdentityStoreRegistry ldapStoreRegistry;
@Override
public UserFederationProvider create(KeycloakSession session) {
throw new IllegalAccessError("Illegal to call this method");
@ -48,13 +45,13 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact
@Override
public LDAPFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model) {
PartitionManagerProvider idmProvider = session.getProvider(PartitionManagerProvider.class);
PartitionManager partition = idmProvider.getPartitionManager(model);
return new LDAPFederationProvider(this, session, model, partition);
LDAPIdentityStore ldapIdentityStore = this.ldapStoreRegistry.getLdapStore(model);
return new LDAPFederationProvider(this, session, model, ldapIdentityStore);
}
@Override
public void init(Config.Scope config) {
this.ldapStoreRegistry = new LDAPIdentityStoreRegistry();
}
@Override
@ -64,7 +61,7 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact
@Override
public void close() {
this.ldapStoreRegistry = null;
}
@Override
@ -81,9 +78,8 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact
public void syncAllUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model) {
logger.infof("Sync all users from LDAP to local store: realm: %s, federation provider: %s, current time: " + new Date(), realmId, model.getDisplayName());
PartitionManagerProvider idmProvider = sessionFactory.create().getProvider(PartitionManagerProvider.class);
PartitionManager partitionMgr = idmProvider.getPartitionManager(model);
IdentityQuery<User> userQuery = partitionMgr.createIdentityManager().createIdentityQuery(User.class);
LDAPIdentityStore ldapIdentityStore = this.ldapStoreRegistry.getLdapStore(model);
IdentityQuery<LDAPUser> userQuery = ldapIdentityStore.createQueryBuilder().createIdentityQuery(LDAPUser.class);
syncImpl(sessionFactory, userQuery, realmId, model);
// TODO: Remove all existing keycloak users, which have federation links, but are not in LDAP. Perhaps don't check users, which were just added or updated during this sync?
@ -91,26 +87,23 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact
@Override
public void syncChangedUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model, Date lastSync) {
logger.infof("Sync changed users from LDAP to local store: realm: %s, federation provider: %s, current time: " + new Date() + ", last sync time: " + lastSync, realmId, model.getDisplayName());
logger.infof("Sync changed users from LDAP to local store: realm: %s, federation provider: %s, current time: %s, last sync time: " + lastSync, realmId, model.getDisplayName(), new Date().toString());
PartitionManagerProvider idmProvider = sessionFactory.create().getProvider(PartitionManagerProvider.class);
PartitionManager partitionMgr = idmProvider.getPartitionManager(model);
LDAPIdentityStore ldapIdentityStore = this.ldapStoreRegistry.getLdapStore(model);
// Sync newly created users
IdentityManager identityManager = partitionMgr.createIdentityManager();
IdentityQueryBuilder queryBuilder = identityManager.getQueryBuilder();
IdentityQueryBuilder queryBuilder = ldapIdentityStore.createQueryBuilder();
Condition condition = queryBuilder.greaterThanOrEqualTo(IdentityType.CREATED_DATE, lastSync);
IdentityQuery<User> userQuery = queryBuilder.createIdentityQuery(User.class).where(condition);
IdentityQuery<LDAPUser> userQuery = queryBuilder.createIdentityQuery(LDAPUser.class).where(condition);
syncImpl(sessionFactory, userQuery, realmId, model);
// Sync updated users
queryBuilder = identityManager.getQueryBuilder();
condition = queryBuilder.greaterThanOrEqualTo(LDAPUtils.MODIFY_DATE, lastSync);
userQuery = queryBuilder.createIdentityQuery(User.class).where(condition);
userQuery = queryBuilder.createIdentityQuery(LDAPUser.class).where(condition);
syncImpl(sessionFactory, userQuery, realmId, model);
}
protected void syncImpl(KeycloakSessionFactory sessionFactory, IdentityQuery<User> userQuery, final String realmId, final UserFederationProviderModel fedModel) {
protected void syncImpl(KeycloakSessionFactory sessionFactory, IdentityQuery<LDAPUser> userQuery, final String realmId, final UserFederationProviderModel fedModel) {
boolean pagination = Boolean.parseBoolean(fedModel.getConfig().get(LDAPConstants.PAGINATION));
if (pagination) {
@ -119,36 +112,36 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact
boolean nextPage = true;
while (nextPage) {
userQuery.setLimit(pageSize);
final List<User> users = userQuery.getResultList();
final List<LDAPUser> users = userQuery.getResultList();
nextPage = userQuery.getPaginationContext() != null;
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
@Override
public void run(KeycloakSession session) {
importPicketlinkUsers(session, realmId, fedModel, users);
importLdapUsers(session, realmId, fedModel, users);
}
});
}
} else {
// LDAP pagination not available. Do everything in single transaction
final List<User> users = userQuery.getResultList();
final List<LDAPUser> users = userQuery.getResultList();
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
@Override
public void run(KeycloakSession session) {
importPicketlinkUsers(session, realmId, fedModel, users);
importLdapUsers(session, realmId, fedModel, users);
}
});
}
}
protected void importPicketlinkUsers(KeycloakSession session, String realmId, UserFederationProviderModel fedModel, List<User> users) {
protected void importLdapUsers(KeycloakSession session, String realmId, UserFederationProviderModel fedModel, List<LDAPUser> ldapUsers) {
RealmModel realm = session.realms().getRealm(realmId);
LDAPFederationProvider ldapFedProvider = getInstance(session, fedModel);
ldapFedProvider.importPicketlinkUsers(realm, users, fedModel);
ldapFedProvider.importLDAPUsers(realm, ldapUsers, fedModel);
}
protected SPNEGOAuthenticator createSPNEGOAuthenticator(String spnegoToken, CommonKerberosConfig kerberosConfig) {

View file

@ -0,0 +1,165 @@
package org.keycloak.federation.ldap;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import org.jboss.logging.Logger;
import org.keycloak.federation.ldap.idm.model.LDAPUser;
import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore;
import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStoreConfiguration;
import org.keycloak.federation.ldap.idm.store.ldap.LDAPMappingConfiguration;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.UserFederationProviderModel;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class LDAPIdentityStoreRegistry {
private static final Logger logger = Logger.getLogger(LDAPIdentityStoreRegistry.class);
private Map<String, LDAPIdentityStoreContext> ldapStores = new ConcurrentHashMap<String, LDAPIdentityStoreContext>();
public LDAPIdentityStore getLdapStore(UserFederationProviderModel model) {
LDAPIdentityStoreContext context = ldapStores.get(model.getId());
// Ldap config might have changed for the realm. In this case, we must re-initialize
Map<String, String> config = model.getConfig();
if (context == null || !config.equals(context.config)) {
logLDAPConfig(model.getId(), config);
LDAPIdentityStore store = createLdapIdentityStore(config);
context = new LDAPIdentityStoreContext(config, store);
ldapStores.put(model.getId(), context);
}
return context.store;
}
// Don't log LDAP password
private void logLDAPConfig(String fedProviderId, Map<String, String> ldapConfig) {
Map<String, String> copy = new HashMap<String, String>(ldapConfig);
copy.remove(LDAPConstants.BIND_CREDENTIAL);
logger.infof("Creating new LDAP based partition manager for the Federation provider: " + fedProviderId + ", LDAP Configuration: " + copy);
}
/**
* @param ldapConfig from realm
* @return PartitionManager instance based on LDAP store
*/
public static LDAPIdentityStore createLdapIdentityStore(Map<String,String> ldapConfig) {
Properties connectionProps = new Properties();
if (ldapConfig.containsKey(LDAPConstants.CONNECTION_POOLING)) {
connectionProps.put("com.sun.jndi.ldap.connect.pool", ldapConfig.get(LDAPConstants.CONNECTION_POOLING));
}
checkSystemProperty("com.sun.jndi.ldap.connect.pool.authentication", "none simple");
checkSystemProperty("com.sun.jndi.ldap.connect.pool.initsize", "1");
checkSystemProperty("com.sun.jndi.ldap.connect.pool.maxsize", "1000");
checkSystemProperty("com.sun.jndi.ldap.connect.pool.prefsize", "5");
checkSystemProperty("com.sun.jndi.ldap.connect.pool.timeout", "300000");
checkSystemProperty("com.sun.jndi.ldap.connect.pool.protocol", "plain");
checkSystemProperty("com.sun.jndi.ldap.connect.pool.debug", "off");
String vendor = ldapConfig.get(LDAPConstants.VENDOR);
boolean activeDirectory = vendor != null && vendor.equals(LDAPConstants.VENDOR_ACTIVE_DIRECTORY);
String ldapLoginNameMapping = ldapConfig.get(LDAPConstants.USERNAME_LDAP_ATTRIBUTE);
if (ldapLoginNameMapping == null) {
ldapLoginNameMapping = activeDirectory ? LDAPConstants.CN : LDAPConstants.UID;
}
String ldapFirstNameMapping = activeDirectory ? "givenName" : LDAPConstants.CN;
String createTimestampMapping = activeDirectory ? "whenCreated" : LDAPConstants.CREATE_TIMESTAMP;
String modifyTimestampMapping = activeDirectory ? "whenChanged" : LDAPConstants.MODIFY_TIMESTAMP;
String[] userObjectClasses = getUserObjectClasses(ldapConfig);
boolean pagination = ldapConfig.containsKey(LDAPConstants.PAGINATION) ? Boolean.parseBoolean(ldapConfig.get(LDAPConstants.PAGINATION)) : false;
boolean userAccountControlsAfterPasswordUpdate = ldapConfig.containsKey(LDAPConstants.USER_ACCOUNT_CONTROLS_AFTER_PASSWORD_UPDATE) ?
Boolean.parseBoolean(ldapConfig.get(LDAPConstants.USER_ACCOUNT_CONTROLS_AFTER_PASSWORD_UPDATE)) : false;
// Differences of unique attribute among various vendors
String uniqueIdentifierAttributeName = LDAPConstants.ENTRY_UUID;
if (vendor != null) {
switch (vendor) {
case LDAPConstants.VENDOR_RHDS:
uniqueIdentifierAttributeName = "nsuniqueid";
break;
case LDAPConstants.VENDOR_TIVOLI:
uniqueIdentifierAttributeName = "uniqueidentifier";
break;
case LDAPConstants.VENDOR_ACTIVE_DIRECTORY:
uniqueIdentifierAttributeName = LDAPConstants.OBJECT_GUID;
}
}
LDAPIdentityStoreConfiguration ldapStoreConfig = new LDAPIdentityStoreConfiguration()
.setConnectionProperties(connectionProps)
.setBaseDN(ldapConfig.get(LDAPConstants.BASE_DN))
.setBindDN(ldapConfig.get(LDAPConstants.BIND_DN))
.setBindCredential(ldapConfig.get(LDAPConstants.BIND_CREDENTIAL))
.setLdapURL(ldapConfig.get(LDAPConstants.CONNECTION_URL))
.setActiveDirectory(activeDirectory)
.setPagination(pagination)
.setUniqueIdentifierAttributeName(uniqueIdentifierAttributeName)
.setFactoryName("com.sun.jndi.ldap.LdapCtxFactory")
.setAuthType("simple")
.setUserAccountControlsAfterPasswordUpdate(userAccountControlsAfterPasswordUpdate);
LDAPMappingConfiguration ldapUserMappingConfig = ldapStoreConfig
.mappingConfig(LDAPUser.class)
.setBaseDN(ldapConfig.get(LDAPConstants.USER_DN_SUFFIX))
.setObjectClasses(new HashSet<String>(Arrays.asList(userObjectClasses)))
.setIdPropertyName("loginName")
.addAttributeMapping("loginName", ldapLoginNameMapping)
.addAttributeMapping("firstName", ldapFirstNameMapping)
.addAttributeMapping("lastName", LDAPConstants.SN)
.addAttributeMapping("email", LDAPConstants.EMAIL)
.addReadOnlyAttributeMapping("createdDate", createTimestampMapping)
.addReadOnlyAttributeMapping("modifyDate", modifyTimestampMapping);
if (activeDirectory && ldapLoginNameMapping.equals("sAMAccountName")) {
ldapUserMappingConfig.setBindingPropertyName("fullName");
ldapUserMappingConfig.addAttributeMapping("fullName", LDAPConstants.CN);
logger.infof("Using 'cn' attribute for DN of user and 'sAMAccountName' for username");
}
return new LDAPIdentityStore(ldapStoreConfig);
}
private static void checkSystemProperty(String name, String defaultValue) {
if (System.getProperty(name) == null) {
System.setProperty(name, defaultValue);
}
}
// Parse array of strings like [ "inetOrgPerson", "organizationalPerson" ] from the string like: "inetOrgPerson, organizationalPerson"
private static String[] getUserObjectClasses(Map<String,String> ldapConfig) {
String objClassesCfg = ldapConfig.get(LDAPConstants.USER_OBJECT_CLASSES);
String objClassesStr = (objClassesCfg != null && objClassesCfg.length() > 0) ? objClassesCfg.trim() : "inetOrgPerson, organizationalPerson";
String[] objectClasses = objClassesStr.split(",");
// Trim them
String[] userObjectClasses = new String[objectClasses.length];
for (int i=0 ; i<objectClasses.length ; i++) {
userObjectClasses[i] = objectClasses[i].trim();
}
return userObjectClasses;
}
private class LDAPIdentityStoreContext {
private LDAPIdentityStoreContext(Map<String,String> config, LDAPIdentityStore store) {
this.config = config;
this.store = store;
}
private Map<String,String> config;
private LDAPIdentityStore store;
}
}

View file

@ -1,22 +1,21 @@
package org.keycloak.federation.ldap;
import org.keycloak.federation.ldap.idm.model.Attribute;
import org.keycloak.federation.ldap.idm.model.LDAPUser;
import org.keycloak.federation.ldap.idm.query.AttributeParameter;
import org.keycloak.federation.ldap.idm.query.IdentityQuery;
import org.keycloak.federation.ldap.idm.query.IdentityQueryBuilder;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelDuplicateException;
import org.picketlink.idm.IdentityManagementException;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.credential.Credentials;
import org.picketlink.idm.credential.Password;
import org.picketlink.idm.credential.UsernamePasswordCredentials;
import org.picketlink.idm.model.Attribute;
import org.picketlink.idm.model.basic.BasicModel;
import org.picketlink.idm.model.basic.User;
import org.picketlink.idm.query.AttributeParameter;
import org.picketlink.idm.query.QueryParameter;
import org.keycloak.models.UserModel;
import java.util.List;
/**
* Allow to directly call some operations against Picketlink IDM PartitionManager (hence LDAP).
* Allow to directly call some operations against LDAPIdentityStore.
* TODO: Is this class still needed?
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@ -24,99 +23,102 @@ public class LDAPUtils {
public static QueryParameter MODIFY_DATE = new AttributeParameter("modifyDate");
public static User addUser(PartitionManager partitionManager, String username, String firstName, String lastName, String email) {
IdentityManager identityManager = getIdentityManager(partitionManager);
if (BasicModel.getUser(identityManager, username) != null) {
public static LDAPUser addUser(LDAPIdentityStore ldapIdentityStore, String username, String firstName, String lastName, String email) {
if (getUser(ldapIdentityStore, username) != null) {
throw new ModelDuplicateException("User with same username already exists");
}
if (getUserByEmail(identityManager, email) != null) {
if (getUserByEmail(ldapIdentityStore, email) != null) {
throw new ModelDuplicateException("User with same email already exists");
}
User picketlinkUser = new User(username);
picketlinkUser.setFirstName(firstName);
picketlinkUser.setLastName(lastName);
picketlinkUser.setEmail(email);
picketlinkUser.setAttribute(new Attribute("fullName", getFullName(username, firstName, lastName)));
identityManager.add(picketlinkUser);
return picketlinkUser;
LDAPUser ldapUser = new LDAPUser(username);
ldapUser.setFirstName(firstName);
ldapUser.setLastName(lastName);
ldapUser.setEmail(email);
ldapUser.setAttribute(new Attribute<String>("fullName", getFullName(username, firstName, lastName)));
ldapIdentityStore.add(ldapUser);
return ldapUser;
}
public static User updateUser(PartitionManager partitionManager, String username, String firstName, String lastName, String email) {
IdentityManager idmManager = getIdentityManager(partitionManager);
User picketlinkUser = BasicModel.getUser(idmManager, username);
picketlinkUser.setFirstName(firstName);
picketlinkUser.setLastName(lastName);
picketlinkUser.setEmail(email);
idmManager.update(picketlinkUser);
return picketlinkUser;
public static LDAPUser updateUser(LDAPIdentityStore ldapIdentityStore, String username, String firstName, String lastName, String email) {
LDAPUser ldapUser = getUser(ldapIdentityStore, username);
ldapUser.setFirstName(firstName);
ldapUser.setLastName(lastName);
ldapUser.setEmail(email);
ldapIdentityStore.update(ldapUser);
return ldapUser;
}
public static void updatePassword(PartitionManager partitionManager, User picketlinkUser, String password) {
IdentityManager idmManager = getIdentityManager(partitionManager);
idmManager.updateCredential(picketlinkUser, new Password(password.toCharArray()));
public static void updatePassword(LDAPIdentityStore ldapIdentityStore, UserModel user, String password) {
LDAPUser ldapUser = convertUserForPasswordUpdate(user);
ldapIdentityStore.updatePassword(ldapUser, password);
}
public static boolean validatePassword(PartitionManager partitionManager, String username, String password) {
IdentityManager idmManager = getIdentityManager(partitionManager);
public static void updatePassword(LDAPIdentityStore ldapIdentityStore, LDAPUser user, String password) {
ldapIdentityStore.updatePassword(user, password);
}
UsernamePasswordCredentials credential = new UsernamePasswordCredentials();
credential.setUsername(username);
credential.setPassword(new Password(password.toCharArray()));
idmManager.validateCredentials(credential);
if (credential.getStatus() == Credentials.Status.VALID) {
return true;
} else {
return false;
public static boolean validatePassword(LDAPIdentityStore ldapIdentityStore, UserModel user, String password) {
LDAPUser ldapUser = convertUserForPasswordUpdate(user);
return ldapIdentityStore.validatePassword(ldapUser, password);
}
public static boolean validatePassword(LDAPIdentityStore ldapIdentityStore, LDAPUser user, String password) {
return ldapIdentityStore.validatePassword(user, password);
}
public static LDAPUser getUser(LDAPIdentityStore ldapIdentityStore, String username) {
return ldapIdentityStore.getUser(username);
}
// Put just username and entryDN as these are needed by LDAPIdentityStore for passwordUpdate
private static LDAPUser convertUserForPasswordUpdate(UserModel kcUser) {
LDAPUser ldapUser = new LDAPUser(kcUser.getUsername());
String ldapEntryDN = kcUser.getAttribute(LDAPConstants.LDAP_ENTRY_DN);
if (ldapEntryDN != null) {
ldapUser.setEntryDN(ldapEntryDN);
}
}
public static User getUser(PartitionManager partitionManager, String username) {
IdentityManager idmManager = getIdentityManager(partitionManager);
return BasicModel.getUser(idmManager, username);
return ldapUser;
}
public static User getUserByEmail(IdentityManager idmManager, String email) throws IdentityManagementException {
List<User> agents = idmManager.createIdentityQuery(User.class)
.setParameter(User.EMAIL, email).getResultList();
public static LDAPUser getUserByEmail(LDAPIdentityStore ldapIdentityStore, String email) {
IdentityQueryBuilder queryBuilder = ldapIdentityStore.createQueryBuilder();
IdentityQuery<LDAPUser> query = queryBuilder.createIdentityQuery(LDAPUser.class)
.where(queryBuilder.equal(LDAPUser.EMAIL, email));
List<LDAPUser> users = query.getResultList();
if (agents.isEmpty()) {
if (users.isEmpty()) {
return null;
} else if (agents.size() == 1) {
return agents.get(0);
} else if (users.size() == 1) {
return users.get(0);
} else {
throw new IdentityManagementException("Error - multiple users found with same email");
throw new ModelDuplicateException("Error - multiple users found with same email " + email);
}
}
public static boolean removeUser(PartitionManager partitionManager, String username) {
IdentityManager idmManager = getIdentityManager(partitionManager);
User picketlinkUser = BasicModel.getUser(idmManager, username);
if (picketlinkUser == null) {
public static boolean removeUser(LDAPIdentityStore ldapIdentityStore, String username) {
LDAPUser ldapUser = getUser(ldapIdentityStore, username);
if (ldapUser == null) {
return false;
}
idmManager.remove(picketlinkUser);
ldapIdentityStore.remove(ldapUser);
return true;
}
public static void removeAllUsers(PartitionManager partitionManager) {
IdentityManager idmManager = getIdentityManager(partitionManager);
List<User> users = idmManager.createIdentityQuery(User.class).getResultList();
public static void removeAllUsers(LDAPIdentityStore ldapIdentityStore) {
List<LDAPUser> allUsers = getAllUsers(ldapIdentityStore);
for (User user : users) {
idmManager.remove(user);
for (LDAPUser user : allUsers) {
ldapIdentityStore.remove(user);
}
}
public static List<User> getAllUsers(PartitionManager partitionManager) {
IdentityManager idmManager = getIdentityManager(partitionManager);
return idmManager.createIdentityQuery(User.class).getResultList();
}
private static IdentityManager getIdentityManager(PartitionManager partitionManager) {
return partitionManager.createIdentityManager();
public static List<LDAPUser> getAllUsers(LDAPIdentityStore ldapIdentityStore) {
IdentityQuery<LDAPUser> userQuery = ldapIdentityStore.createQueryBuilder().createIdentityQuery(LDAPUser.class);
return userQuery.getResultList();
}
// Needed for ActiveDirectory updates

View file

@ -1,16 +1,11 @@
package org.keycloak.federation.ldap;
import org.jboss.logging.Logger;
import org.keycloak.models.ModelException;
import org.keycloak.federation.ldap.idm.model.LDAPUser;
import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.UserModelDelegate;
import org.picketlink.idm.IdentityManagementException;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.credential.Password;
import org.picketlink.idm.credential.TOTPCredential;
import org.picketlink.idm.model.basic.BasicModel;
import org.picketlink.idm.model.basic.User;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -28,52 +23,43 @@ public class WritableLDAPUserModelDelegate extends UserModelDelegate implements
@Override
public void setUsername(String username) {
IdentityManager identityManager = provider.getIdentityManager();
LDAPIdentityStore ldapIdentityStore = provider.getLdapIdentityStore();
try {
User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
if (picketlinkUser == null) {
throw new IllegalStateException("User not found in LDAP storage!");
}
picketlinkUser.setLoginName(username);
identityManager.update(picketlinkUser);
} catch (IdentityManagementException ie) {
throw new ModelException(ie);
LDAPUser ldapUser = LDAPUtils.getUser(ldapIdentityStore, delegate.getUsername());
if (ldapUser == null) {
throw new IllegalStateException("User not found in LDAP storage!");
}
ldapUser.setLoginName(username);
ldapIdentityStore.update(ldapUser);
delegate.setUsername(username);
}
@Override
public void setLastName(String lastName) {
IdentityManager identityManager = provider.getIdentityManager();
LDAPIdentityStore ldapIdentityStore = provider.getLdapIdentityStore();
try {
User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
if (picketlinkUser == null) {
throw new IllegalStateException("User not found in LDAP storage!");
}
picketlinkUser.setLastName(lastName);
identityManager.update(picketlinkUser);
} catch (IdentityManagementException ie) {
throw new ModelException(ie);
LDAPUser ldapUser = LDAPUtils.getUser(ldapIdentityStore, delegate.getUsername());
if (ldapUser == null) {
throw new IllegalStateException("User not found in LDAP storage!");
}
ldapUser.setLastName(lastName);
ldapIdentityStore.update(ldapUser);
delegate.setLastName(lastName);
}
@Override
public void setFirstName(String first) {
IdentityManager identityManager = provider.getIdentityManager();
LDAPIdentityStore ldapIdentityStore = provider.getLdapIdentityStore();
try {
User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
if (picketlinkUser == null) {
throw new IllegalStateException("User not found in LDAP storage!");
}
picketlinkUser.setFirstName(first);
identityManager.update(picketlinkUser);
} catch (IdentityManagementException ie) {
throw new ModelException(ie);
LDAPUser ldapUser = LDAPUtils.getUser(ldapIdentityStore, delegate.getUsername());
if (ldapUser == null) {
throw new IllegalStateException("User not found in LDAP storage!");
}
ldapUser.setFirstName(first);
ldapIdentityStore.update(ldapUser);
delegate.setFirstName(first);
}
@ -83,41 +69,31 @@ public class WritableLDAPUserModelDelegate extends UserModelDelegate implements
delegate.updateCredential(cred);
return;
}
IdentityManager identityManager = provider.getIdentityManager();
try {
User picketlinkUser = BasicModel.getUser(identityManager, getUsername());
if (picketlinkUser == null) {
logger.debugf("User '%s' doesn't exists. Skip password update", getUsername());
throw new IllegalStateException("User doesn't exist in LDAP storage");
}
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
identityManager.updateCredential(picketlinkUser, new Password(cred.getValue().toCharArray()));
} else if (cred.getType().equals(UserCredentialModel.TOTP)) {
TOTPCredential credential = new TOTPCredential(cred.getValue());
credential.setDevice(cred.getDevice());
identityManager.updateCredential(picketlinkUser, credential);
}
} catch (IdentityManagementException ie) {
throw new ModelException(ie);
LDAPIdentityStore ldapIdentityStore = provider.getLdapIdentityStore();
LDAPUser ldapUser = LDAPUtils.getUser(ldapIdentityStore, delegate.getUsername());
if (ldapUser == null) {
throw new IllegalStateException("User " + delegate.getUsername() + " not found in LDAP storage!");
}
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
LDAPUtils.updatePassword(ldapIdentityStore, delegate, cred.getValue());
} else {
logger.warnf("Don't know how to update credential of type [%s] for user [%s]", cred.getType(), delegate.getUsername());
}
}
@Override
public void setEmail(String email) {
IdentityManager identityManager = provider.getIdentityManager();
LDAPIdentityStore ldapIdentityStore = provider.getLdapIdentityStore();
try {
User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
if (picketlinkUser == null) {
throw new IllegalStateException("User not found in LDAP storage!");
}
picketlinkUser.setEmail(email);
identityManager.update(picketlinkUser);
} catch (IdentityManagementException ie) {
throw new ModelException(ie);
LDAPUser ldapUser = LDAPUtils.getUser(ldapIdentityStore, delegate.getUsername());
if (ldapUser == null) {
throw new IllegalStateException("User not found in LDAP storage!");
}
ldapUser.setEmail(email);
ldapIdentityStore.update(ldapUser);
delegate.setEmail(email);
}

View file

@ -0,0 +1,85 @@
package org.keycloak.federation.ldap.idm.model;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.unmodifiableMap;
/**
* Abstract base class for all AttributedType implementations
*
* @author Shane Bryzak
*
*/
public abstract class AbstractAttributedType implements AttributedType {
private static final long serialVersionUID = -6118293036241099199L;
private String id;
private String entryDN;
private Map<String, Attribute<? extends Serializable>> attributes =
new HashMap<String, Attribute<? extends Serializable>>();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getEntryDN() {
return entryDN;
}
public void setEntryDN(String entryDN) {
this.entryDN = entryDN;
}
public void setAttribute(Attribute<? extends Serializable> attribute) {
attributes.put(attribute.getName(), attribute);
}
public void removeAttribute(String name) {
attributes.remove(name);
}
@SuppressWarnings("unchecked")
public <T extends Serializable> Attribute<T> getAttribute(String name) {
return (Attribute<T>) attributes.get(name);
}
public Collection<Attribute<? extends Serializable>> getAttributes() {
return unmodifiableCollection(attributes.values());
}
public Map<String,Attribute<? extends Serializable>> getAttributesMap() {
return unmodifiableMap(attributes);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!getClass().isInstance(obj)) {
return false;
}
AttributedType other = (AttributedType) obj;
return getId() != null && other.getId() != null && getId().equals(other.getId());
}
@Override
public int hashCode() {
int result = getId() != null ? getId().hashCode() : 0;
result = 31 * result + (getId() != null ? getId().hashCode() : 0);
return result;
}
}

View file

@ -0,0 +1,70 @@
package org.keycloak.federation.ldap.idm.model;
import java.util.Date;
/**
* Abstract base class for IdentityType implementations
*
* @author Shane Bryzak
*/
public abstract class AbstractIdentityType extends AbstractAttributedType implements IdentityType {
private static final long serialVersionUID = 2843998332737143820L;
private boolean enabled = true;
private Date createdDate = new Date();
private Date expirationDate = null;
public boolean isEnabled() {
return this.enabled;
}
@Override
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
@AttributeProperty
public Date getExpirationDate() {
return this.expirationDate;
}
@Override
public void setExpirationDate(Date expirationDate) {
this.expirationDate = expirationDate;
}
@Override
@AttributeProperty
public Date getCreatedDate() {
return this.createdDate;
}
@Override
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!getClass().isInstance(obj)) {
return false;
}
IdentityType other = (IdentityType) obj;
return (getId() != null && other.getId() != null)
&& (getId().equals(other.getId()));
}
@Override
public int hashCode() {
return super.hashCode();
}
}

View file

@ -0,0 +1,80 @@
package org.keycloak.federation.ldap.idm.model;
import java.io.Serializable;
/**
* Represents an attribute value, a type of metadata that can be associated with an IdentityType
*
* @author Shane Bryzak
*
* @param <T>
*/
public class Attribute<T extends Serializable> implements Serializable {
private static final long serialVersionUID = 237211288303510728L;
/**
* The name of the attribute
*/
private String name;
/**
* The attribute value.
*/
private T value;
/**
* Indicates whether this Attribute has a read-only value
*/
private boolean readOnly = false;
/**
* Indicates whether the Attribute value has been loaded
*/
private boolean loaded = false;
public Attribute(String name, T value) {
this.name = name;
this.value = value;
}
public Attribute(String name, T value, boolean readOnly) {
this(name, value);
this.readOnly = readOnly;
}
public String getName() {
return name;
}
public T getValue() {
return value;
}
public boolean isReadOnly() {
return readOnly;
}
public boolean isLoaded() {
return loaded;
}
public void setLoaded(boolean value) {
this.loaded = value;
}
/**
* Sets the value for this attribute. If the Attribute value is readOnly, a RuntimeException is thrown.
*
* @param value
*/
public void setValue(T value) {
if (readOnly) {
throw new RuntimeException("Error setting Attribute value [" + name + " ] - value is read only.");
}
this.value = value;
}
}

View file

@ -0,0 +1,31 @@
package org.keycloak.federation.ldap.idm.model;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Marks a property of an IdentityType, Partition or Relationship as being an attribute of that
* IdentityType, Partition or Relationship.
*
* @author Shane Bryzak
*/
@Target({METHOD, FIELD})
@Documented
@Retention(RUNTIME)
@Inherited
public @interface AttributeProperty {
/**
* <p>Managed properties are stored as ad-hoc attributes and mapped from and to a specific property of a type.</p>
*
* @return
*/
boolean managed() default false;
}

View file

@ -0,0 +1,75 @@
package org.keycloak.federation.ldap.idm.model;
import java.io.Serializable;
import java.util.Collection;
import org.keycloak.federation.ldap.idm.query.AttributeParameter;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
/**
*
* @author Shane Bryzak
*
*/
public interface AttributedType extends Serializable {
/**
* A query parameter used to set the id value.
*/
QueryParameter ID = new AttributeParameter("id");
/**
* Returns the unique identifier for this instance
* @return
*/
String getId();
/**
* Sets the unique identifier for this instance
* @return
*/
void setId(String id);
/**
* Set the specified attribute. This operation will overwrite any previous value.
*
* @param attribute to be set
*/
void setAttribute(Attribute<? extends Serializable> attribute);
/**
* Remove the attribute with given name
*
* @param name of attribute
*/
void removeAttribute(String name);
// LDAP specific stuff
void setEntryDN(String entryDN);
String getEntryDN();
/**
* Return the attribute value with the specified name
*
* @param name of attribute
* @return attribute value or null if attribute with given name doesn't exist. If given attribute has many values method
* will return first one
*/
<T extends Serializable> Attribute<T> getAttribute(String name);
/**
* Returns a Map containing all attribute values for this IdentityType instance.
*
* @return map of attribute names and their values
*/
Collection<Attribute<? extends Serializable>> getAttributes();
public final class QUERY_ATTRIBUTE {
public static AttributeParameter byName(String name) {
return new AttributeParameter(name);
}
}
}

View file

@ -0,0 +1,100 @@
package org.keycloak.federation.ldap.idm.model;
import java.util.Date;
import org.keycloak.federation.ldap.idm.query.AttributeParameter;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
/**
* This interface is the base for all identity model objects. It declares a number of
* properties that must be supported by all identity types, in addition to defining the API
* for identity attribute management.
*
* @author Shane Bryzak
*/
public interface IdentityType extends AttributedType {
/**
* A query parameter used to set the enabled value.
*/
QueryParameter ENABLED = new AttributeParameter("enabled");
/**
* A query parameter used to set the createdDate value
*/
QueryParameter CREATED_DATE = new AttributeParameter("createdDate");
/**
* A query parameter used to set the created after date
*/
QueryParameter CREATED_AFTER = new AttributeParameter("createdDate");
/**
* A query parameter used to set the modified after date
*/
QueryParameter MODIFIED_AFTER = new AttributeParameter("modifyDate");
/**
* A query parameter used to set the created before date
*/
QueryParameter CREATED_BEFORE = new AttributeParameter("createdDate");
/**
* A query parameter used to set the expiryDate value
*/
QueryParameter EXPIRY_DATE = new AttributeParameter("expirationDate");
/**
* A query parameter used to set the expiration after date
*/
QueryParameter EXPIRY_AFTER = new AttributeParameter("expirationDate");
/**
* A query parameter used to set the expiration before date
*/
QueryParameter EXPIRY_BEFORE = new AttributeParameter("expirationDate");
/**
* Indicates the current enabled status of this IdentityType.
*
* @return A boolean value indicating whether this IdentityType is enabled.
*/
boolean isEnabled();
/**
* <p>Sets the current enabled status of this {@link IdentityType}.</p>
*
* @param enabled
*/
void setEnabled(boolean enabled);
/**
* Returns the date that this IdentityType instance was created.
*
* @return Date value representing the creation date
*/
Date getCreatedDate();
/**
* <p>Sets the date that this {@link IdentityType} was created.</p>
*
* @param createdDate
*/
void setCreatedDate(Date createdDate);
/**
* Returns the date that this IdentityType expires, or null if there is no expiry date.
*
* @return
*/
Date getExpirationDate();
/**
* <p>Sets the date that this {@link IdentityType} expires.</p>
*
* @param expirationDate
*/
void setExpirationDate(Date expirationDate);
}

View file

@ -0,0 +1,85 @@
package org.keycloak.federation.ldap.idm.model;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
/**
* This class represents a User; a human agent that may authenticate with the application
*
* @author Shane Bryzak
*/
public class LDAPUser extends AbstractIdentityType {
private static final long serialVersionUID = 4117586097100398485L;
public static final QueryParameter LOGIN_NAME = AttributedType.QUERY_ATTRIBUTE.byName("loginName");
/**
* A query parameter used to set the firstName value.
*/
public static final QueryParameter FIRST_NAME = QUERY_ATTRIBUTE.byName("firstName");
/**
* A query parameter used to set the lastName value.
*/
public static final QueryParameter LAST_NAME = QUERY_ATTRIBUTE.byName("lastName");
/**
* A query parameter used to set the email value.
*/
public static final QueryParameter EMAIL = QUERY_ATTRIBUTE.byName("email");
@AttributeProperty
private String loginName;
@AttributeProperty
private String firstName;
@AttributeProperty
private String lastName;
@AttributeProperty
private String email;
public LDAPUser() {
}
public LDAPUser(String loginName) {
this.loginName = loginName;
}
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
}

View file

@ -0,0 +1,21 @@
package org.keycloak.federation.ldap.idm.query;
/**
* <p>This class can be used to define a query parameter for properties annotated with
* {@link org.keycloak.federation.ldap.idm.model.AttributeProperty}.
* </p>
*
* @author pedroigor
*/
public class AttributeParameter implements QueryParameter {
private final String name;
public AttributeParameter(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}

View file

@ -0,0 +1,18 @@
package org.keycloak.federation.ldap.idm.query;
/**
* <p>A {@link Condition} is used to specify how a specific {@link QueryParameter}
* is defined in order to filter query results.</p>
*
* @author Pedro Igor
*/
public interface Condition {
/**
* <p>The {@link QueryParameter} restricted by this condition.</p>
*
* @return
*/
QueryParameter getParameter();
}

View file

@ -0,0 +1,225 @@
package org.keycloak.federation.ldap.idm.query;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.keycloak.federation.ldap.idm.model.IdentityType;
/**
* <p>An {@link IdentityQuery} is responsible for querying the underlying identity stores for instances of
* a given {@link IdentityType}.</p>
*
* <p>Instances of this class are obtained using the {@link IdentityQueryBuilder#createIdentityQuery(Class)}
* method.</p>
*
* <pre>
* IdentityManager identityManager = getIdentityManager();
*
* // here we get the query builder
* IdentityQueryBuilder builder = identityManager.getQueryBuilder();
*
* // create a condition
* Condition condition = builder.equal(User.LOGIN_NAME, "john");
*
* // create a query for a specific identity type using the previously created condition
* IdentityQuery query = builder.createIdentityQuery(User.class).where(condition);
*
* // execute the query
* List<User> result = query.getResultList();
* </pre>
*
* <p>When preparing a query you may want to create conditions to filter its results and configure how they must be retrieved.
* For that, you can use the {@link IdentityQueryBuilder}, which provides useful methods for creating
* different expressions and conditions.</p>
*
* @author Shane Bryzak
* @author Pedro Igor
*/
public interface IdentityQuery<T extends IdentityType> {
/**
* @see #setPaginationContext(Object object)
*/
Object getPaginationContext();
/**
* Used for pagination models like LDAP when search will return some object (like cookie) for searching on next page
*
* @param object to be used for search next page
*
* @return this query
*/
IdentityQuery<T> setPaginationContext(Object object);
/**
* @deprecated Will be removed soon.
*
* @see #setSortParameters(QueryParameter...)
*/
@Deprecated
QueryParameter[] getSortParameters();
/**
* Parameters used to sort the results. First parameter has biggest priority. For example: setSortParameter(User.LAST_NAME,
* User.FIRST_NAME) means that results will be sorted primarily by lastName and firstName will be used to sort only records with
* same lastName
*
* @param sortParameters parameters to specify sort criteria
*
* @deprecated Use {@link IdentityQuery#sortBy(Sort...)} instead. Where you can create sort conditions
* from the {@link IdentityQueryBuilder}.
*
* @return this query
*/
@Deprecated
IdentityQuery<T> setSortParameters(QueryParameter... sortParameters);
/**
* @deprecated Use {@link IdentityQuery#getSorting()} for a list of sorting conditions. Will be removed soon.
*
* @return true if sorting will be ascending
*
* @see #setSortAscending(boolean)
*/
@Deprecated
boolean isSortAscending();
/**
* Specify if sorting will be ascending (true) or descending (false)
*
* @param sortAscending to specify if sorting will be ascending or descending
*
* @deprecated Use {@link IdentityQuery#sortBy(Sort...)} instead. Where you can create sort conditions
* from the {@link IdentityQueryBuilder}.
*
* @return this query
*/
@Deprecated
IdentityQuery<T> setSortAscending(boolean sortAscending);
/**
* <p>Set a query parameter to this query in order to filter the results.</p>
*
* <p>This method always create an equality condition. For more conditions options take a look at {@link
* IdentityQueryBuilder} and use the {@link IdentityQuery#where(Condition...)}
* instead.</p>
*
* @param param The query parameter.
* @param value The value to match for equality.
*
* @return
*
* @deprecated Use {@link IdentityQuery#where(Condition...)} to specify query conditions.
*/
@Deprecated
IdentityQuery<T> setParameter(QueryParameter param, Object... value);
/**
* <p>Add to this query the conditions that will be used to filter results.</p>
*
* <p>Any condition previously added to this query will be preserved and the new conditions added. If you want to clear the
* conditions you must create a new query instance.</p>
*
* @param condition One or more conditions created from {@link IdentityQueryBuilder}.
*
* @return
*/
IdentityQuery<T> where(Condition... condition);
/**
* <p>Add to this query the sorting conditions to be applied to the results.</p>
*
* @param sorts The ordering conditions.
*
* @return
*/
IdentityQuery<T> sortBy(Sort... sorts);
/**
* <p>The type used to create this query.</p>
*
* @return
*/
Class<T> getIdentityType();
/**
* <p>Returns a map with all the parameter set for this query.</p>
*
* @return
*
* @deprecated Use {@link IdentityQuery#getConditions()} instead. Will be removed.
*/
@Deprecated
Map<QueryParameter, Object[]> getParameters();
/**
* <p>Returns a set containing all conditions used by this query to filter its results.</p>
*
* @return
*/
Set<Condition> getConditions();
/**
* <p>Returns a set containing all sorting conditions used to filter the results.</p>
*
* @return
*/
Set<Sort> getSorting();
/**
* <p>Returns the value used to restrict the given query parameter.</p>
*
* @param queryParameter
*
* @return
*/
@Deprecated
Object[] getParameter(QueryParameter queryParameter);
@Deprecated
Map<QueryParameter, Object[]> getParameters(Class<?> type);
int getOffset();
/**
* <p>Set the position of the first result to retrieve.</p>
*
* @param offset
*
* @return
*/
IdentityQuery<T> setOffset(int offset);
/**
* <p>Returns the number of instances to retrieve.</p>
*
* @return
*/
int getLimit();
/**
* <p>Set the maximum number of results to retrieve.</p>
*
* @param limit the number of instances to retrieve.
*
* @return
*/
IdentityQuery<T> setLimit(int limit);
/**
* <p>Execute the query against the underlying identity stores and returns a list containing all instances of
* the type (defined when creating this query instance) that match the conditions previously specified.</p>
*
* @return
*/
List<T> getResultList();
/**
* Count of all query results. It takes into account query parameters, but it doesn't take into account pagination parameter
* like offset and limit
*
* @return count of all query results
*/
int getResultCount();
}

View file

@ -0,0 +1,124 @@
package org.keycloak.federation.ldap.idm.query;
import org.keycloak.federation.ldap.idm.model.IdentityType;
/**
* <p>The {@link IdentityQueryBuilder} is responsible for creating {@link IdentityQuery} instances and also
* provide methods to create conditions, orderings, sorting, etc.</p>
*
* @author Pedro Igor
*/
public interface IdentityQueryBuilder {
/**
* <p>Create a condition for testing the whether the query parameter satisfies the given pattern..</p>
*
* @param parameter The query parameter.
* @param pattern The pattern to match.
*
* @return
*/
Condition like(QueryParameter parameter, String pattern);
/**
* <p>Create a condition for testing the arguments for equality.</p>
*
* @param parameter The query parameter.
* @param value The value to compare.
*
* @return
*/
Condition equal(QueryParameter parameter, Object value);
/**
* <p>Create a condition for testing whether the query parameter is grater than the given value..</p>
*
* @param parameter The query parameter.
* @param x The value to compare.
*
* @return
*/
Condition greaterThan(QueryParameter parameter, Object x);
/**
* <p>Create a condition for testing whether the query parameter is grater than or equal to the given value..</p>
*
* @param parameter The query parameter.
* @param x The value to compare.
*
* @return
*/
Condition greaterThanOrEqualTo(QueryParameter parameter, Object x);
/**
* <p>Create a condition for testing whether the query parameter is less than the given value..</p>
*
* @param parameter The query parameter.
* @param x The value to compare.
*
* @return
*/
Condition lessThan(QueryParameter parameter, Object x);
/**
* <p>Create a condition for testing whether the query parameter is less than or equal to the given value..</p>
*
* @param parameter The query parameter.
* @param x The value to compare.
*
* @return
*/
Condition lessThanOrEqualTo(QueryParameter parameter, Object x);
/**
* <p>Create a condition for testing whether the query parameter is between the given values.</p>
*
* @param parameter The query parameter.
* @param x The first value.
* @param x The second value.
*
* @return
*/
Condition between(QueryParameter parameter, Object x, Object y);
/**
* <p>Create a condition for testing whether the query parameter is contained in a list of values.</p>
*
* @param parameter The query parameter.
* @param values A list of values.
*
* @return
*/
Condition in(QueryParameter parameter, Object... values);
/**
* <p>Create an ascending order for the given <code>parameter</code>. Once created, you can use it to sort the results of a
* query.</p>
*
* @param parameter The query parameter to sort.
*
* @return
*/
Sort asc(QueryParameter parameter);
/**
* <p>Create an descending order for the given <code>parameter</code>. Once created, you can use it to sort the results of a
* query.</p>
*
* @param parameter The query parameter to sort.
*
* @return
*/
Sort desc(QueryParameter parameter);
/**
* <p> Create an {@link IdentityQuery} that can be used to query for {@link
* IdentityType} instances of a the given <code>identityType</code>. </p>
*
* @param identityType The type to search. If you provide the {@link IdentityType}
* base interface any of its sub-types will be returned.
*
* @return
*/
<T extends IdentityType> IdentityQuery<T> createIdentityQuery(Class<T> identityType);
}

View file

@ -0,0 +1,12 @@
package org.keycloak.federation.ldap.idm.query;
/**
* A marker interface indicating that the implementing class can be used as a
* parameter within an IdentityQuery or RelationshipQuery
*
* @author Shane Bryzak
*
*/
public interface QueryParameter {
}

View file

@ -0,0 +1,23 @@
package org.keycloak.federation.ldap.idm.query;
/**
* @author Pedro Igor
*/
public class Sort {
private final QueryParameter parameter;
private final boolean asc;
public Sort(QueryParameter parameter, boolean asc) {
this.parameter = parameter;
this.asc = asc;
}
public QueryParameter getParameter() {
return this.parameter;
}
public boolean isAscending() {
return asc;
}
}

View file

@ -0,0 +1,33 @@
package org.keycloak.federation.ldap.idm.query.internal;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
/**
* @author Pedro Igor
*/
public class BetweenCondition implements Condition {
private final Comparable x;
private final Comparable y;
private final QueryParameter parameter;
public BetweenCondition(QueryParameter parameter, Comparable x, Comparable y) {
this.parameter = parameter;
this.x = x;
this.y = y;
}
@Override
public QueryParameter getParameter() {
return this.parameter;
}
public Comparable getX() {
return this.x;
}
public Comparable getY() {
return this.y;
}
}

View file

@ -0,0 +1,207 @@
package org.keycloak.federation.ldap.idm.query.internal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.keycloak.federation.ldap.idm.model.IdentityType;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.IdentityQuery;
import org.keycloak.federation.ldap.idm.query.IdentityQueryBuilder;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
import org.keycloak.federation.ldap.idm.query.Sort;
import org.keycloak.federation.ldap.idm.store.IdentityStore;
import org.keycloak.models.ModelException;
import static java.util.Collections.unmodifiableSet;
/**
* Default IdentityQuery implementation.
*
* @param <T>
*
* @author Shane Bryzak
*/
public class DefaultIdentityQuery<T extends IdentityType> implements IdentityQuery<T> {
private final Map<QueryParameter, Object[]> parameters = new LinkedHashMap<QueryParameter, Object[]>();
private final Class<T> identityType;
private final IdentityStore identityStore;
private final IdentityQueryBuilder queryBuilder;
private int offset;
private int limit;
private Object paginationContext;
private QueryParameter[] sortParameters;
private boolean sortAscending = true;
private final Set<Condition> conditions = new LinkedHashSet<Condition>();
private final Set<Sort> ordering = new LinkedHashSet<Sort>();
public DefaultIdentityQuery(IdentityQueryBuilder queryBuilder, Class<T> identityType, IdentityStore identityStore) {
this.queryBuilder = queryBuilder;
this.identityStore = identityStore;
this.identityType = identityType;
}
@Override
public IdentityQuery<T> setParameter(QueryParameter queryParameter, Object... value) {
if (value == null || value.length == 0) {
throw new ModelException("Query Parameter values null or empty");
}
parameters.put(queryParameter, value);
if (IdentityType.CREATED_AFTER.equals(queryParameter) || IdentityType.EXPIRY_AFTER.equals(queryParameter)) {
this.conditions.add(queryBuilder.greaterThanOrEqualTo(queryParameter, value[0]));
} else if (IdentityType.CREATED_BEFORE.equals(queryParameter) || IdentityType.EXPIRY_BEFORE.equals(queryParameter)) {
this.conditions.add(queryBuilder.lessThanOrEqualTo(queryParameter, value[0]));
} else {
this.conditions.add(queryBuilder.equal(queryParameter, value[0]));
}
return this;
}
@Override
public IdentityQuery<T> where(Condition... condition) {
this.conditions.addAll(Arrays.asList(condition));
return this;
}
@Override
public IdentityQuery<T> sortBy(Sort... sorts) {
this.ordering.addAll(Arrays.asList(sorts));
return this;
}
@Override
public Set<Sort> getSorting() {
return unmodifiableSet(this.ordering);
}
@Override
public Class<T> getIdentityType() {
return identityType;
}
@Override
public Map<QueryParameter, Object[]> getParameters() {
return parameters;
}
@Override
public Object[] getParameter(QueryParameter queryParameter) {
return this.parameters.get(queryParameter);
}
@Override
public Map<QueryParameter, Object[]> getParameters(Class<?> type) {
Map<QueryParameter, Object[]> typedParameters = new HashMap<QueryParameter, Object[]>();
Set<Map.Entry<QueryParameter, Object[]>> entrySet = this.parameters.entrySet();
for (Map.Entry<QueryParameter, Object[]> entry : entrySet) {
if (type.isInstance(entry.getKey())) {
typedParameters.put(entry.getKey(), entry.getValue());
}
}
return typedParameters;
}
@Override
public int getLimit() {
return limit;
}
@Override
public int getOffset() {
return offset;
}
@Override
public Object getPaginationContext() {
return paginationContext;
}
@Override
public QueryParameter[] getSortParameters() {
return sortParameters;
}
@Override
public boolean isSortAscending() {
return sortAscending;
}
@Override
public List<T> getResultList() {
// remove this statement once deprecated methods on IdentityQuery are removed
if (this.sortParameters != null) {
for (QueryParameter parameter : this.sortParameters) {
if (isSortAscending()) {
sortBy(this.queryBuilder.asc(parameter));
} else {
sortBy(this.queryBuilder.desc(parameter));
}
}
}
List<T> result = new ArrayList<T>();
try {
for (T identityType : identityStore.fetchQueryResults(this)) {
result.add(identityType);
}
} catch (Exception e) {
throw new ModelException("LDAP Query failed", e);
}
return result;
}
@Override
public int getResultCount() {
return identityStore.countQueryResults(this);
}
@Override
public IdentityQuery<T> setOffset(int offset) {
this.offset = offset;
return this;
}
@Override
public IdentityQuery<T> setLimit(int limit) {
this.limit = limit;
return this;
}
@Override
public IdentityQuery<T> setSortParameters(QueryParameter... sortParameters) {
this.sortParameters = sortParameters;
return this;
}
@Override
public IdentityQuery<T> setSortAscending(boolean sortAscending) {
this.sortAscending = sortAscending;
return this;
}
@Override
public IdentityQuery<T> setPaginationContext(Object object) {
this.paginationContext = object;
return this;
}
@Override
public Set<Condition> getConditions() {
return unmodifiableSet(this.conditions);
}
}

View file

@ -0,0 +1,89 @@
package org.keycloak.federation.ldap.idm.query.internal;
import org.keycloak.federation.ldap.idm.model.IdentityType;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.IdentityQuery;
import org.keycloak.federation.ldap.idm.query.IdentityQueryBuilder;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
import org.keycloak.federation.ldap.idm.query.Sort;
import org.keycloak.federation.ldap.idm.store.IdentityStore;
import org.keycloak.models.ModelException;
/**
* @author Pedro Igor
*/
public class DefaultQueryBuilder implements IdentityQueryBuilder {
private final IdentityStore identityStore;
public DefaultQueryBuilder(IdentityStore identityStore) {
this.identityStore = identityStore;
}
@Override
public Condition like(QueryParameter parameter, String pattern) {
return new LikeCondition(parameter, pattern);
}
@Override
public Condition equal(QueryParameter parameter, Object value) {
return new EqualCondition(parameter, value);
}
@Override
public Condition greaterThan(QueryParameter parameter, Object x) {
throwExceptionIfNotComparable(x);
return new GreaterThanCondition(parameter, (Comparable) x, false);
}
@Override
public Condition greaterThanOrEqualTo(QueryParameter parameter, Object x) {
throwExceptionIfNotComparable(x);
return new GreaterThanCondition(parameter, (Comparable) x, true);
}
@Override
public Condition lessThan(QueryParameter parameter, Object x) {
throwExceptionIfNotComparable(x);
return new LessThanCondition(parameter, (Comparable) x, false);
}
@Override
public Condition lessThanOrEqualTo(QueryParameter parameter, Object x) {
throwExceptionIfNotComparable(x);
return new LessThanCondition(parameter, (Comparable) x, true);
}
@Override
public Condition between(QueryParameter parameter, Object x, Object y) {
throwExceptionIfNotComparable(x);
throwExceptionIfNotComparable(y);
return new BetweenCondition(parameter, (Comparable) x, (Comparable) y);
}
@Override
public Condition in(QueryParameter parameter, Object... x) {
return new InCondition(parameter, x);
}
@Override
public Sort asc(QueryParameter parameter) {
return new Sort(parameter, true);
}
@Override
public Sort desc(QueryParameter parameter) {
return new Sort(parameter, false);
}
@Override
public <T extends IdentityType> IdentityQuery createIdentityQuery(Class<T> identityType) {
return new DefaultIdentityQuery(this, identityType, this.identityStore);
}
private void throwExceptionIfNotComparable(Object x) {
if (!Comparable.class.isInstance(x)) {
throw new ModelException("Query parameter value [" + x + "] must be " + Comparable.class + ".");
}
}
}

View file

@ -0,0 +1,36 @@
package org.keycloak.federation.ldap.idm.query.internal;
import org.keycloak.federation.ldap.idm.query.AttributeParameter;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
/**
* @author Pedro Igor
*/
public class EqualCondition implements Condition {
private final QueryParameter parameter;
private final Object value;
public EqualCondition(QueryParameter parameter, Object value) {
this.parameter = parameter;
this.value = value;
}
@Override
public QueryParameter getParameter() {
return this.parameter;
}
public Object getValue() {
return this.value;
}
@Override
public String toString() {
return "EqualCondition{" +
"parameter=" + ((AttributeParameter) parameter).getName() +
", value=" + value +
'}';
}
}

View file

@ -0,0 +1,34 @@
package org.keycloak.federation.ldap.idm.query.internal;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
/**
* @author Pedro Igor
*/
public class GreaterThanCondition implements Condition {
private final boolean orEqual;
private final QueryParameter parameter;
private final Comparable value;
public GreaterThanCondition(QueryParameter parameter, Comparable value, boolean orEqual) {
this.parameter = parameter;
this.value = value;
this.orEqual = orEqual;
}
@Override
public QueryParameter getParameter() {
return this.parameter;
}
public Comparable getValue() {
return this.value;
}
public boolean isOrEqual() {
return this.orEqual;
}
}

View file

@ -0,0 +1,28 @@
package org.keycloak.federation.ldap.idm.query.internal;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
/**
* @author Pedro Igor
*/
public class InCondition implements Condition {
private final QueryParameter parameter;
private final Object[] value;
public InCondition(QueryParameter parameter, Object[] value) {
this.parameter = parameter;
this.value = value;
}
@Override
public QueryParameter getParameter() {
return this.parameter;
}
public Object[] getValue() {
return this.value;
}
}

View file

@ -0,0 +1,34 @@
package org.keycloak.federation.ldap.idm.query.internal;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
/**
* @author Pedro Igor
*/
public class LessThanCondition implements Condition {
private final boolean orEqual;
private final QueryParameter parameter;
private final Comparable value;
public LessThanCondition(QueryParameter parameter, Comparable value, boolean orEqual) {
this.parameter = parameter;
this.value = value;
this.orEqual = orEqual;
}
@Override
public QueryParameter getParameter() {
return this.parameter;
}
public Comparable getValue() {
return this.value;
}
public boolean isOrEqual() {
return this.orEqual;
}
}

View file

@ -0,0 +1,28 @@
package org.keycloak.federation.ldap.idm.query.internal;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
/**
* @author Pedro Igor
*/
public class LikeCondition implements Condition {
private final QueryParameter parameter;
private final Object value;
public LikeCondition(QueryParameter parameter, Object value) {
this.parameter = parameter;
this.value = value;
}
@Override
public QueryParameter getParameter() {
return this.parameter;
}
public Object getValue() {
return this.value;
}
}

View file

@ -0,0 +1,81 @@
package org.keycloak.federation.ldap.idm.store;
import java.util.List;
import org.keycloak.federation.ldap.idm.model.AttributedType;
import org.keycloak.federation.ldap.idm.model.IdentityType;
import org.keycloak.federation.ldap.idm.model.LDAPUser;
import org.keycloak.federation.ldap.idm.query.IdentityQuery;
import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStoreConfiguration;
/**
* IdentityStore representation providing minimal SPI
*
* TODO: Rather remove this abstraction
*
* @author Boleslaw Dawidowicz
* @author Shane Bryzak
*/
public interface IdentityStore {
/**
* Returns the configuration for this IdentityStore instance
*
* @return
*/
LDAPIdentityStoreConfiguration getConfig();
// General
/**
* Persists the specified IdentityType
*
* @param value
*/
void add(AttributedType value);
/**
* Updates the specified IdentityType
*
* @param value
*/
void update(AttributedType value);
/**
* Removes the specified IdentityType
*
* @param value
*/
void remove(AttributedType value);
// Identity query
<V extends IdentityType> List<V> fetchQueryResults(IdentityQuery<V> identityQuery);
<V extends IdentityType> int countQueryResults(IdentityQuery<V> identityQuery);
// // Relationship query
//
// <V extends Relationship> List<V> fetchQueryResults(RelationshipQuery<V> query);
//
// <V extends Relationship> int countQueryResults(RelationshipQuery<V> query);
// Credentials
/**
* Validates the specified credentials.
*
* @param user Keycloak user
* @param password Ldap password
*/
boolean validatePassword(LDAPUser user, String password);
/**
* Updates the specified credential value.
*
* @param user Keycloak user
* @param password Ldap password
*/
void updatePassword(LDAPUser user, String password);
}

View file

@ -0,0 +1,761 @@
package org.keycloak.federation.ldap.idm.store.ldap;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchResult;
import org.jboss.logging.Logger;
import org.keycloak.federation.ldap.idm.model.AttributedType;
import org.keycloak.federation.ldap.idm.model.IdentityType;
import org.keycloak.federation.ldap.idm.model.LDAPUser;
import org.keycloak.federation.ldap.idm.query.AttributeParameter;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.IdentityQuery;
import org.keycloak.federation.ldap.idm.query.IdentityQueryBuilder;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
import org.keycloak.federation.ldap.idm.query.internal.BetweenCondition;
import org.keycloak.federation.ldap.idm.query.internal.DefaultQueryBuilder;
import org.keycloak.federation.ldap.idm.query.internal.EqualCondition;
import org.keycloak.federation.ldap.idm.query.internal.GreaterThanCondition;
import org.keycloak.federation.ldap.idm.query.internal.InCondition;
import org.keycloak.federation.ldap.idm.query.internal.LessThanCondition;
import org.keycloak.federation.ldap.idm.query.internal.LikeCondition;
import org.keycloak.federation.ldap.idm.store.IdentityStore;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.ModelException;
import org.keycloak.models.utils.reflection.NamedPropertyCriteria;
import org.keycloak.models.utils.reflection.Property;
import org.keycloak.models.utils.reflection.PropertyQueries;
import org.keycloak.models.utils.reflection.TypedPropertyCriteria;
import org.keycloak.util.reflections.Reflections;
/**
* An IdentityStore implementation backed by an LDAP directory
*
* @author Shane Bryzak
* @author Anil Saldhana
* @author <a href="mailto:psilva@redhat.com">Pedro Silva</a>
*/
public class LDAPIdentityStore implements IdentityStore {
private static final Logger logger = Logger.getLogger(LDAPIdentityStore.class);
public static final String EMPTY_ATTRIBUTE_VALUE = " ";
private final LDAPIdentityStoreConfiguration config;
private final LDAPOperationManager operationManager;
public LDAPIdentityStore(LDAPIdentityStoreConfiguration config) {
this.config = config;
try {
this.operationManager = new LDAPOperationManager(getConfig());
} catch (NamingException e) {
throw new ModelException("Couldn't init operation manager", e);
}
}
@Override
public LDAPIdentityStoreConfiguration getConfig() {
return this.config;
}
@Override
public void add(AttributedType attributedType) {
// id will be assigned by the ldap server
attributedType.setId(null);
String entryDN = getBindingDN(attributedType, true);
this.operationManager.createSubContext(entryDN, extractAttributes(attributedType, true));
addToParentAsMember(attributedType);
attributedType.setId(getEntryIdentifier(attributedType));
attributedType.setEntryDN(entryDN);
if (logger.isTraceEnabled()) {
logger.tracef("Type with identifier [%s] successfully added to identity store [%s].", attributedType.getId(), this);
}
}
@Override
public void update(AttributedType attributedType) {
BasicAttributes updatedAttributes = extractAttributes(attributedType, false);
NamingEnumeration<Attribute> attributes = updatedAttributes.getAll();
this.operationManager.modifyAttributes(getBindingDN(attributedType, true), attributes);
if (logger.isTraceEnabled()) {
logger.tracef("Type with identifier [%s] successfully updated to identity store [%s].", attributedType.getId(), this);
}
}
@Override
public void remove(AttributedType attributedType) {
LDAPMappingConfiguration mappingConfig = getMappingConfig(attributedType.getClass());
this.operationManager.removeEntryById(getBaseDN(attributedType), attributedType.getId(), mappingConfig);
if (logger.isTraceEnabled()) {
logger.tracef("Type with identifier [%s] successfully removed from identity store [%s].", attributedType.getId(), this);
}
}
@Override
public <V extends IdentityType> List<V> fetchQueryResults(IdentityQuery<V> identityQuery) {
List<V> results = new ArrayList<V>();
try {
if (identityQuery.getSorting() != null && !identityQuery.getSorting().isEmpty()) {
throw new ModelException("LDAP Identity Store does not support sorted queries.");
}
for (Condition condition : identityQuery.getConditions()) {
if (IdentityType.ID.equals(condition.getParameter())) {
if (EqualCondition.class.isInstance(condition)) {
EqualCondition equalCondition = (EqualCondition) condition;
SearchResult search = this.operationManager
.lookupById(getConfig().getBaseDN(), equalCondition.getValue().toString(), null);
if (search != null) {
results.add((V) populateAttributedType(search, null));
}
}
return results;
}
}
if (!IdentityType.class.equals(identityQuery.getIdentityType())) {
// the ldap store does not support queries based on root types. Except if based on the identifier.
LDAPMappingConfiguration ldapEntryConfig = getMappingConfig(identityQuery.getIdentityType());
StringBuilder filter = createIdentityTypeSearchFilter(identityQuery, ldapEntryConfig);
String baseDN = getBaseDN(ldapEntryConfig);
List<SearchResult> search;
if (getConfig().isPagination() && identityQuery.getLimit() > 0) {
search = this.operationManager.searchPaginated(baseDN, filter.toString(), ldapEntryConfig, identityQuery);
} else {
search = this.operationManager.search(baseDN, filter.toString(), ldapEntryConfig);
}
for (SearchResult result : search) {
if (!result.getNameInNamespace().equals(baseDN)) {
results.add((V) populateAttributedType(result, null));
}
}
}
} catch (Exception e) {
throw new ModelException("Querying of identity type failed " + identityQuery, e);
}
return results;
}
@Override
public <V extends IdentityType> int countQueryResults(IdentityQuery<V> identityQuery) {
int limit = identityQuery.getLimit();
int offset = identityQuery.getOffset();
identityQuery.setLimit(0);
identityQuery.setOffset(0);
int resultCount = identityQuery.getResultList().size();
identityQuery.setLimit(limit);
identityQuery.setOffset(offset);
return resultCount;
}
public IdentityQueryBuilder createQueryBuilder() {
return new DefaultQueryBuilder(this);
}
// *************** CREDENTIALS AND USER SPECIFIC STUFF
@Override
public boolean validatePassword(LDAPUser user, String password) {
String userDN = getEntryDNOfUser(user);
if (logger.isDebugEnabled()) {
logger.debugf("Using DN [%s] for authentication of user [%s]", userDN, user.getLoginName());
}
if (operationManager.authenticate(userDN, password)) {
return true;
}
return false;
}
@Override
public void updatePassword(LDAPUser user, String password) {
String userDN = getEntryDNOfUser(user);
if (logger.isDebugEnabled()) {
logger.debugf("Using DN [%s] for updating LDAP password of user [%s]", userDN, user.getLoginName());
}
if (getConfig().isActiveDirectory()) {
updateADPassword(userDN, password);
} else {
ModificationItem[] mods = new ModificationItem[1];
try {
BasicAttribute mod0 = new BasicAttribute(LDAPConstants.USER_PASSWORD_ATTRIBUTE, password);
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, mod0);
operationManager.modifyAttribute(userDN, mod0);
} catch (Exception e) {
throw new ModelException("Error updating password.", e);
}
}
}
private void updateADPassword(String userDN, String password) {
try {
// Replace the "unicdodePwd" attribute with a new value
// Password must be both Unicode and a quoted string
String newQuotedPassword = "\"" + password + "\"";
byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
BasicAttribute unicodePwd = new BasicAttribute("unicodePwd", newUnicodePassword);
List<ModificationItem> modItems = new ArrayList<ModificationItem>();
modItems.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE, unicodePwd));
// Used in ActiveDirectory to put account into "enabled" state (aka userAccountControl=512, see http://support.microsoft.com/kb/305144/en ) after password update. If value is -1, it's ignored
if (getConfig().isUserAccountControlsAfterPasswordUpdate()) {
BasicAttribute userAccountControl = new BasicAttribute("userAccountControl", "512");
modItems.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE, userAccountControl));
logger.debugf("Attribute userAccountControls will be switched to 512 after password update of user [%s]", userDN);
}
operationManager.modifyAttributes(userDN, modItems.toArray(new ModificationItem[] {}));
} catch (Exception e) {
throw new ModelException(e);
}
}
private String getEntryDNOfUser(LDAPUser user) {
// First try if user already has entryDN on him
String entryDN = user.getEntryDN();
if (entryDN != null) {
return entryDN;
}
// Need to find user in LDAP
String username = user.getLoginName();
user = getUser(username);
if (user == null) {
throw new ModelException("No LDAP user found with username " + username);
}
return user.getEntryDN();
}
public LDAPUser getUser(String username) {
if (isNullOrEmpty(username)) {
return null;
}
IdentityQueryBuilder queryBuilder = createQueryBuilder();
List<LDAPUser> agents = queryBuilder.createIdentityQuery(LDAPUser.class)
.where(queryBuilder.equal(LDAPUser.LOGIN_NAME, username)).getResultList();
if (agents.isEmpty()) {
return null;
} else if (agents.size() == 1) {
return agents.get(0);
} else {
throw new ModelDuplicateException("Error - multiple Agent objects found with same login name");
}
}
// ************ END CREDENTIALS AND USER SPECIFIC STUFF
private String getBaseDN(final LDAPMappingConfiguration ldapEntryConfig) {
String baseDN = getConfig().getBaseDN();
if (ldapEntryConfig.getBaseDN() != null) {
baseDN = ldapEntryConfig.getBaseDN();
}
return baseDN;
}
protected <V extends IdentityType> StringBuilder createIdentityTypeSearchFilter(final IdentityQuery<V> identityQuery, final LDAPMappingConfiguration ldapEntryConfig) {
StringBuilder filter = new StringBuilder();
for (Condition condition : identityQuery.getConditions()) {
QueryParameter queryParameter = condition.getParameter();
if (!IdentityType.ID.equals(queryParameter)) {
if (AttributeParameter.class.isInstance(queryParameter)) {
AttributeParameter attributeParameter = (AttributeParameter) queryParameter;
String attributeName = ldapEntryConfig.getMappedProperties().get(attributeParameter.getName());
if (attributeName != null) {
if (EqualCondition.class.isInstance(condition)) {
EqualCondition equalCondition = (EqualCondition) condition;
Object parameterValue = equalCondition.getValue();
if (Date.class.isInstance(parameterValue)) {
parameterValue = LDAPUtil.formatDate((Date) parameterValue);
}
filter.append("(").append(attributeName).append(LDAPConstants.EQUAL).append(parameterValue).append(")");
} else if (LikeCondition.class.isInstance(condition)) {
LikeCondition likeCondition = (LikeCondition) condition;
String parameterValue = (String) likeCondition.getValue();
} else if (GreaterThanCondition.class.isInstance(condition)) {
GreaterThanCondition greaterThanCondition = (GreaterThanCondition) condition;
Comparable parameterValue = (Comparable) greaterThanCondition.getValue();
if (Date.class.isInstance(parameterValue)) {
parameterValue = LDAPUtil.formatDate((Date) parameterValue);
}
if (greaterThanCondition.isOrEqual()) {
filter.append("(").append(attributeName).append(">=").append(parameterValue).append(")");
} else {
filter.append("(").append(attributeName).append(">").append(parameterValue).append(")");
}
} else if (LessThanCondition.class.isInstance(condition)) {
LessThanCondition lessThanCondition = (LessThanCondition) condition;
Comparable parameterValue = (Comparable) lessThanCondition.getValue();
if (Date.class.isInstance(parameterValue)) {
parameterValue = LDAPUtil.formatDate((Date) parameterValue);
}
if (lessThanCondition.isOrEqual()) {
filter.append("(").append(attributeName).append("<=").append(parameterValue).append(")");
} else {
filter.append("(").append(attributeName).append("<").append(parameterValue).append(")");
}
} else if (BetweenCondition.class.isInstance(condition)) {
BetweenCondition betweenCondition = (BetweenCondition) condition;
Comparable x = betweenCondition.getX();
Comparable y = betweenCondition.getY();
if (Date.class.isInstance(x)) {
x = LDAPUtil.formatDate((Date) x);
}
if (Date.class.isInstance(y)) {
y = LDAPUtil.formatDate((Date) y);
}
filter.append("(").append(x).append("<=").append(attributeName).append("<=").append(y).append(")");
} else if (InCondition.class.isInstance(condition)) {
InCondition inCondition = (InCondition) condition;
Object[] valuesToCompare = inCondition.getValue();
filter.append("(&(");
for (int i = 0; i< valuesToCompare.length; i++) {
Object value = valuesToCompare[i];
filter.append("(").append(attributeName).append(LDAPConstants.EQUAL).append(value).append(")");
}
filter.append("))");
} else {
throw new ModelException("Unsupported query condition [" + condition + "].");
}
}
}
}
}
filter.insert(0, "(&(");
filter.append(getObjectClassesFilter(ldapEntryConfig));
filter.append("))");
return filter;
}
private StringBuilder getObjectClassesFilter(final LDAPMappingConfiguration ldapEntryConfig) {
StringBuilder builder = new StringBuilder();
if (ldapEntryConfig != null && !ldapEntryConfig.getObjectClasses().isEmpty()) {
for (String objectClass : ldapEntryConfig.getObjectClasses()) {
builder.append("(").append(LDAPConstants.OBJECT_CLASS).append(LDAPConstants.EQUAL).append(objectClass).append(")");
}
} else {
builder.append("(").append(LDAPConstants.OBJECT_CLASS).append(LDAPConstants.EQUAL).append("*").append(")");
}
return builder;
}
private AttributedType populateAttributedType(SearchResult searchResult, AttributedType attributedType) {
return populateAttributedType(searchResult, attributedType, 0);
}
private AttributedType populateAttributedType(SearchResult searchResult, AttributedType attributedType, int hierarchyDepthCount) {
try {
String entryDN = searchResult.getNameInNamespace();
Attributes attributes = searchResult.getAttributes();
if (attributedType == null) {
attributedType = Reflections.newInstance(getConfig().getSupportedTypeByBaseDN(entryDN, getEntryObjectClasses(attributes)));
}
attributedType.setEntryDN(entryDN);
LDAPMappingConfiguration mappingConfig = getMappingConfig(attributedType.getClass());
if (hierarchyDepthCount > mappingConfig.getHierarchySearchDepth()) {
return null;
}
if (logger.isTraceEnabled()) {
logger.tracef("Populating attributed type [%s] from DN [%s]", attributedType, entryDN);
}
NamingEnumeration<? extends Attribute> ldapAttributes = attributes.getAll();
while (ldapAttributes.hasMore()) {
Attribute ldapAttribute = ldapAttributes.next();
Object attributeValue;
try {
attributeValue = ldapAttribute.get();
} catch (NoSuchElementException nsee) {
continue;
}
String ldapAttributeName = ldapAttribute.getID();
if (ldapAttributeName.toLowerCase().equals(getConfig().getUniqueIdentifierAttributeName().toLowerCase())) {
attributedType.setId(this.operationManager.decodeEntryUUID(attributeValue));
} else {
String attributeName = findAttributeName(mappingConfig.getMappedProperties(), ldapAttributeName);
if (attributeName != null) {
// Find if it's java property or attribute
Property<Object> property = PropertyQueries
.createQuery(attributedType.getClass())
.addCriteria(new NamedPropertyCriteria(attributeName)).getFirstResult();
if (property != null) {
if (logger.isTraceEnabled()) {
logger.tracef("Populating property [%s] from ldap attribute [%s] with value [%s] from DN [%s].", property.getName(), ldapAttributeName, attributeValue, entryDN);
}
if (property.getJavaClass().equals(Date.class)) {
property.setValue(attributedType, LDAPUtil.parseDate(attributeValue.toString()));
} else {
property.setValue(attributedType, attributeValue);
}
} else {
if (logger.isTraceEnabled()) {
logger.tracef("Populating attribute [%s] from ldap attribute [%s] with value [%s] from DN [%s].", attributeName, ldapAttributeName, attributeValue, entryDN);
}
attributedType.setAttribute(new org.keycloak.federation.ldap.idm.model.Attribute(attributeName, (Serializable) attributeValue));
}
}
}
}
if (IdentityType.class.isInstance(attributedType)) {
IdentityType identityType = (IdentityType) attributedType;
String createdTimestamp = attributes.get(LDAPConstants.CREATE_TIMESTAMP).get().toString();
identityType.setCreatedDate(LDAPUtil.parseDate(createdTimestamp));
}
LDAPMappingConfiguration entryConfig = getMappingConfig(attributedType.getClass());
if (mappingConfig.getParentMembershipAttributeName() != null) {
StringBuilder filter = new StringBuilder("(&");
String entryBaseDN = entryDN.substring(entryDN.indexOf(LDAPConstants.COMMA) + 1);
filter
.append("(")
.append(getObjectClassesFilter(entryConfig))
.append(")")
.append("(")
.append(mappingConfig.getParentMembershipAttributeName())
.append(LDAPConstants.EQUAL).append("")
.append(getBindingDN(attributedType, false))
.append(LDAPConstants.COMMA)
.append(entryBaseDN)
.append(")");
filter.append(")");
if (logger.isTraceEnabled()) {
logger.tracef("Searching parent entry for DN [%s] using filter [%s].", entryBaseDN, filter.toString());
}
List<SearchResult> search = this.operationManager.search(getConfig().getBaseDN(), filter.toString(), entryConfig);
if (!search.isEmpty()) {
SearchResult next = search.get(0);
Property<AttributedType> parentProperty = PropertyQueries
.<AttributedType>createQuery(attributedType.getClass())
.addCriteria(new TypedPropertyCriteria(attributedType.getClass())).getFirstResult();
if (parentProperty != null) {
String parentDN = next.getNameInNamespace();
String parentBaseDN = parentDN.substring(parentDN.indexOf(",") + 1);
Class<? extends AttributedType> baseDNType = getConfig().getSupportedTypeByBaseDN(parentBaseDN, getEntryObjectClasses(attributes));
if (parentProperty.getJavaClass().isAssignableFrom(baseDNType)) {
if (logger.isTraceEnabled()) {
logger.tracef("Found parent [%s] for entry for DN [%s].", parentDN, entryDN);
}
int hierarchyDepthCount1 = ++hierarchyDepthCount;
parentProperty.setValue(attributedType, populateAttributedType(next, null, hierarchyDepthCount1));
}
}
} else {
if (logger.isTraceEnabled()) {
logger.tracef("No parent entry found for DN [%s] using filter [%s].", entryDN, filter.toString());
}
}
}
} catch (Exception e) {
throw new ModelException("Could not populate attribute type " + attributedType + ".", e);
}
return attributedType;
}
private String findAttributeName(Map<String, String> attrMapping, String ldapAttributeName) {
for (Map.Entry<String,String> currentAttr : attrMapping.entrySet()) {
if (currentAttr.getValue().equalsIgnoreCase(ldapAttributeName)) {
return currentAttr.getKey();
}
}
return null;
}
private List<String> getEntryObjectClasses(final Attributes attributes) throws NamingException {
Attribute objectClassesAttribute = attributes.get(LDAPConstants.OBJECT_CLASS);
List<String> objectClasses = new ArrayList<String>();
if (objectClassesAttribute == null) {
return objectClasses;
}
NamingEnumeration<?> all = objectClassesAttribute.getAll();
while (all.hasMore()) {
objectClasses.add(all.next().toString());
}
return objectClasses;
}
protected BasicAttributes extractAttributes(AttributedType attributedType, boolean isCreate) {
BasicAttributes entryAttributes = new BasicAttributes();
LDAPMappingConfiguration mappingConfig = getMappingConfig(attributedType.getClass());
Map<String, String> mappedProperties = mappingConfig.getMappedProperties();
for (String propertyName : mappedProperties.keySet()) {
if (!mappingConfig.getReadOnlyAttributes().contains(propertyName) && (isCreate || !mappingConfig.getBindingProperty().getName().equals(propertyName))) {
Property<Object> property = PropertyQueries
.<Object>createQuery(attributedType.getClass())
.addCriteria(new NamedPropertyCriteria(propertyName)).getFirstResult();
Object propertyValue = null;
if (property != null) {
// Mapped Java property on the object
propertyValue = property.getValue(attributedType);
} else {
// Not mapped property. So fallback to attribute
org.keycloak.federation.ldap.idm.model.Attribute<?> attribute = attributedType.getAttribute(propertyName);
if (attribute != null) {
propertyValue = attribute.getValue();
}
}
if (AttributedType.class.isInstance(propertyValue)) {
AttributedType referencedType = (AttributedType) propertyValue;
propertyValue = getBindingDN(referencedType, true);
} else {
if (propertyValue == null || isNullOrEmpty(propertyValue.toString())) {
propertyValue = EMPTY_ATTRIBUTE_VALUE;
}
}
entryAttributes.put(mappedProperties.get(propertyName), propertyValue);
}
}
// Don't extract object classes for update
if (isCreate) {
LDAPMappingConfiguration ldapEntryConfig = getMappingConfig(attributedType.getClass());
BasicAttribute objectClassAttribute = new BasicAttribute(LDAPConstants.OBJECT_CLASS);
for (String objectClassValue : ldapEntryConfig.getObjectClasses()) {
objectClassAttribute.add(objectClassValue);
if (objectClassValue.equals(LDAPConstants.GROUP_OF_NAMES)
|| objectClassValue.equals(LDAPConstants.GROUP_OF_ENTRIES)
|| objectClassValue.equals(LDAPConstants.GROUP_OF_UNIQUE_NAMES)) {
entryAttributes.put(LDAPConstants.MEMBER, EMPTY_ATTRIBUTE_VALUE);
}
}
entryAttributes.put(objectClassAttribute);
}
return entryAttributes;
}
// TODO: Move class StringUtil from SAML module
public static boolean isNullOrEmpty(String str) {
return str == null || str.isEmpty();
}
private LDAPMappingConfiguration getMappingConfig(Class<? extends AttributedType> attributedType) {
LDAPMappingConfiguration mappingConfig = getConfig().getMappingConfig(attributedType);
if (mappingConfig == null) {
throw new ModelException("Not mapped type [" + attributedType + "].");
}
return mappingConfig;
}
public String getBindingDN(AttributedType attributedType, boolean appendBaseDN) {
LDAPMappingConfiguration mappingConfig = getMappingConfig(attributedType.getClass());
Property<String> idProperty = mappingConfig.getIdProperty();
String baseDN;
if (mappingConfig.getBaseDN() == null || !appendBaseDN) {
baseDN = "";
} else {
baseDN = LDAPConstants.COMMA + getBaseDN(attributedType);
}
Property<String> bindingProperty = mappingConfig.getBindingProperty();
String bindingAttribute;
String dn;
if (bindingProperty == null) {
bindingAttribute = mappingConfig.getMappedProperties().get(idProperty.getName());
dn = idProperty.getValue(attributedType);
} else {
bindingAttribute = mappingConfig.getMappedProperties().get(bindingProperty.getName());
dn = mappingConfig.getBindingProperty().getValue(attributedType);
}
return bindingAttribute + LDAPConstants.EQUAL + dn + baseDN;
}
private String getBaseDN(AttributedType attributedType) {
LDAPMappingConfiguration mappingConfig = getMappingConfig(attributedType.getClass());
String baseDN = mappingConfig.getBaseDN();
String parentDN = mappingConfig.getParentMapping().get(mappingConfig.getIdProperty().getValue(attributedType));
if (parentDN != null) {
baseDN = parentDN;
} else {
Property<AttributedType> parentProperty = PropertyQueries
.<AttributedType>createQuery(attributedType.getClass())
.addCriteria(new TypedPropertyCriteria(attributedType.getClass())).getFirstResult();
if (parentProperty != null) {
AttributedType parentType = parentProperty.getValue(attributedType);
if (parentType != null) {
Property<String> parentIdProperty = getMappingConfig(parentType.getClass()).getIdProperty();
String parentId = parentIdProperty.getValue(parentType);
String parentBaseDN = mappingConfig.getParentMapping().get(parentId);
if (parentBaseDN != null) {
baseDN = parentBaseDN;
} else {
baseDN = getBaseDN(parentType);
}
}
}
}
if (baseDN == null) {
baseDN = getConfig().getBaseDN();
}
return baseDN;
}
protected void addToParentAsMember(final AttributedType attributedType) {
LDAPMappingConfiguration entryConfig = getMappingConfig(attributedType.getClass());
if (entryConfig.getParentMembershipAttributeName() != null) {
Property<AttributedType> parentProperty = PropertyQueries
.<AttributedType>createQuery(attributedType.getClass())
.addCriteria(new TypedPropertyCriteria(attributedType.getClass()))
.getFirstResult();
if (parentProperty != null) {
AttributedType parentType = parentProperty.getValue(attributedType);
if (parentType != null) {
Attributes attributes = this.operationManager.getAttributes(parentType.getId(), getBaseDN(parentType), entryConfig);
Attribute attribute = attributes.get(entryConfig.getParentMembershipAttributeName());
attribute.add(getBindingDN(attributedType, true));
this.operationManager.modifyAttribute(getBindingDN(parentType, true), attribute);
}
}
}
}
protected String getEntryIdentifier(final AttributedType attributedType) {
try {
// we need this to retrieve the entry's identifier from the ldap server
List<SearchResult> search = this.operationManager.search(getBaseDN(attributedType), "(" + getBindingDN(attributedType, false) + ")", getMappingConfig(attributedType.getClass()));
Attribute id = search.get(0).getAttributes().get(getConfig().getUniqueIdentifierAttributeName());
if (id == null) {
throw new ModelException("Could not retrieve identifier for entry [" + getBindingDN(attributedType, true) + "].");
}
return this.operationManager.decodeEntryUUID(id.get());
} catch (NamingException ne) {
throw new ModelException("Could not add type [" + attributedType + "].", ne);
}
}
}

View file

@ -0,0 +1,188 @@
package org.keycloak.federation.ldap.idm.store.ldap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.keycloak.federation.ldap.idm.model.AttributedType;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelException;
/**
* A configuration for the LDAP store.
*
* @author anil saldhana
* @since Sep 6, 2012
*/
public class LDAPIdentityStoreConfiguration {
private String ldapURL;
private String factoryName = "com.sun.jndi.ldap.LdapCtxFactory";
private String authType = "simple";
private String protocol;
private String bindDN;
private String bindCredential;
private boolean activeDirectory;
private Properties connectionProperties;
private boolean pagination;
private String uniqueIdentifierAttributeName;
private boolean userAccountControlsAfterPasswordUpdate;
private String baseDN;
private Map<Class<? extends AttributedType>, LDAPMappingConfiguration> mappingConfig = new HashMap<Class<? extends AttributedType>, LDAPMappingConfiguration>();
public String getLdapURL() {
return this.ldapURL;
}
public String getFactoryName() {
return this.factoryName;
}
public String getAuthType() {
return this.authType;
}
public String getBaseDN() {
return this.baseDN;
}
public String getBindDN() {
return this.bindDN;
}
public String getBindCredential() {
return this.bindCredential;
}
public boolean isActiveDirectory() {
return this.activeDirectory;
}
public Properties getConnectionProperties() {
return this.connectionProperties;
}
public LDAPMappingConfiguration mappingConfig(Class<? extends AttributedType> clazz) {
LDAPMappingConfiguration mappingConfig = new LDAPMappingConfiguration(clazz);
this.mappingConfig.put(clazz, mappingConfig);
return mappingConfig;
}
public Class<? extends AttributedType> getSupportedTypeByBaseDN(String entryDN, List<String> objectClasses) {
String entryBaseDN = entryDN.substring(entryDN.indexOf(LDAPConstants.COMMA) + 1);
for (LDAPMappingConfiguration mappingConfig : this.mappingConfig.values()) {
if (mappingConfig.getBaseDN() != null) {
if (mappingConfig.getBaseDN().equalsIgnoreCase(entryDN)
|| mappingConfig.getParentMapping().values().contains(entryDN)) {
return mappingConfig.getMappedClass();
}
if (mappingConfig.getBaseDN().equalsIgnoreCase(entryBaseDN)
|| mappingConfig.getParentMapping().values().contains(entryBaseDN)) {
return mappingConfig.getMappedClass();
}
}
}
for (LDAPMappingConfiguration mappingConfig : this.mappingConfig.values()) {
for (String objectClass : objectClasses) {
if (mappingConfig.getObjectClasses().contains(objectClass)) {
return mappingConfig.getMappedClass();
}
}
}
throw new ModelException("No type found with Base DN [" + entryDN + "] or objectClasses [" + objectClasses + ".");
}
public LDAPMappingConfiguration getMappingConfig(Class<? extends AttributedType> attributedType) {
for (LDAPMappingConfiguration mappingConfig : this.mappingConfig.values()) {
if (attributedType.equals(mappingConfig.getMappedClass())) {
return mappingConfig;
}
}
return null;
}
public String getProtocol() {
return protocol;
}
public String getUniqueIdentifierAttributeName() {
return uniqueIdentifierAttributeName;
}
public boolean isPagination() {
return pagination;
}
public boolean isUserAccountControlsAfterPasswordUpdate() {
return userAccountControlsAfterPasswordUpdate;
}
public LDAPIdentityStoreConfiguration setLdapURL(String ldapURL) {
this.ldapURL = ldapURL;
return this;
}
public LDAPIdentityStoreConfiguration setFactoryName(String factoryName) {
this.factoryName = factoryName;
return this;
}
public LDAPIdentityStoreConfiguration setAuthType(String authType) {
this.authType = authType;
return this;
}
public LDAPIdentityStoreConfiguration setProtocol(String protocol) {
this.protocol = protocol;
return this;
}
public LDAPIdentityStoreConfiguration setBindDN(String bindDN) {
this.bindDN = bindDN;
return this;
}
public LDAPIdentityStoreConfiguration setBindCredential(String bindCredential) {
this.bindCredential = bindCredential;
return this;
}
public LDAPIdentityStoreConfiguration setActiveDirectory(boolean activeDirectory) {
this.activeDirectory = activeDirectory;
return this;
}
public LDAPIdentityStoreConfiguration setPagination(boolean pagination) {
this.pagination = pagination;
return this;
}
public LDAPIdentityStoreConfiguration setConnectionProperties(Properties connectionProperties) {
this.connectionProperties = connectionProperties;
return this;
}
public LDAPIdentityStoreConfiguration setUniqueIdentifierAttributeName(String uniqueIdentifierAttributeName) {
this.uniqueIdentifierAttributeName = uniqueIdentifierAttributeName;
return this;
}
public LDAPIdentityStoreConfiguration setUserAccountControlsAfterPasswordUpdate(boolean userAccountControlsAfterPasswordUpdate) {
this.userAccountControlsAfterPasswordUpdate = userAccountControlsAfterPasswordUpdate;
return this;
}
public LDAPIdentityStoreConfiguration setBaseDN(String baseDN) {
this.baseDN = baseDN;
return this;
}
}

View file

@ -0,0 +1,231 @@
package org.keycloak.federation.ldap.idm.store.ldap;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Member;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.keycloak.federation.ldap.idm.model.Attribute;
import org.keycloak.federation.ldap.idm.model.AttributedType;
import org.keycloak.federation.ldap.idm.model.IdentityType;
import org.keycloak.models.ModelException;
import org.keycloak.models.utils.reflection.NamedPropertyCriteria;
import org.keycloak.models.utils.reflection.Property;
import org.keycloak.models.utils.reflection.PropertyQueries;
/**
* @author pedroigor
*/
public class LDAPMappingConfiguration {
private final Class<? extends AttributedType> mappedClass;
private Set<String> objectClasses;
private String baseDN;
private final Map<String, String> mappedProperties = new HashMap<String, String>();
private Property<String> idProperty;
private Class<? extends AttributedType> relatedAttributedType;
private String parentMembershipAttributeName;
private Map<String, String> parentMapping = new HashMap<String, String>();
private final Set<String> readOnlyAttributes = new HashSet<String>();
private int hierarchySearchDepth;
private Property<String> bindingProperty;
public LDAPMappingConfiguration(Class<? extends AttributedType> mappedClass) {
this.mappedClass = mappedClass;
}
public Class<? extends AttributedType> getMappedClass() {
return this.mappedClass;
}
public Set<String> getObjectClasses() {
return this.objectClasses;
}
public String getBaseDN() {
return this.baseDN;
}
public Map<String, String> getMappedProperties() {
return this.mappedProperties;
}
public Property<String> getIdProperty() {
return this.idProperty;
}
public Property<String> getBindingProperty() {
return this.bindingProperty;
}
public Class<? extends AttributedType> getRelatedAttributedType() {
return this.relatedAttributedType;
}
public String getParentMembershipAttributeName() {
return this.parentMembershipAttributeName;
}
public Map<String, String> getParentMapping() {
return this.parentMapping;
}
public Set<String> getReadOnlyAttributes() {
return this.readOnlyAttributes;
}
public int getHierarchySearchDepth() {
return this.hierarchySearchDepth;
}
private Property getBindingProperty(final String bindingPropertyName) {
Property bindingProperty = PropertyQueries
.<String>createQuery(getMappedClass())
.addCriteria(new NamedPropertyCriteria(bindingPropertyName)).getFirstResult();
// We don't have Java property, so actually delegate to setAttribute/getAttribute
if (bindingProperty == null) {
bindingProperty = new Property<String>() {
@Override
public String getName() {
return bindingPropertyName;
}
@Override
public Type getBaseType() {
return null;
}
@Override
public Class<String> getJavaClass() {
return String.class;
}
@Override
public AnnotatedElement getAnnotatedElement() {
return null;
}
@Override
public Member getMember() {
return null;
}
@Override
public String getValue(Object instance) {
if (!(instance instanceof AttributedType)) {
throw new IllegalStateException("Instance [ " + instance + " ] not an instance of AttributedType");
}
AttributedType attributedType = (AttributedType) instance;
Attribute<String> attr = attributedType.getAttribute(bindingPropertyName);
return attr!=null ? attr.getValue() : null;
}
@Override
public void setValue(Object instance, String value) {
if (!(instance instanceof AttributedType)) {
throw new IllegalStateException("Instance [ " + instance + " ] not an instance of AttributedType");
}
AttributedType attributedType = (AttributedType) instance;
attributedType.setAttribute(new Attribute(bindingPropertyName, value));
}
@Override
public Class<?> getDeclaringClass() {
return null;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public void setAccessible() {
}
@Override
public boolean isAnnotationPresent(Class annotation) {
return false;
}
};
}
return bindingProperty;
}
public LDAPMappingConfiguration setObjectClasses(Set<String> objectClasses) {
this.objectClasses = objectClasses;
return this;
}
public LDAPMappingConfiguration setBaseDN(String baseDN) {
this.baseDN = baseDN;
return this;
}
public LDAPMappingConfiguration addAttributeMapping(String userAttributeName, String ldapAttributeName) {
this.mappedProperties.put(userAttributeName, ldapAttributeName);
return this;
}
public LDAPMappingConfiguration addReadOnlyAttributeMapping(String userAttributeName, String ldapAttributeName) {
this.mappedProperties.put(userAttributeName, ldapAttributeName);
this.readOnlyAttributes.add(userAttributeName);
return this;
}
public LDAPMappingConfiguration setIdPropertyName(String idPropertyName) {
if (idPropertyName != null) {
this.idProperty = PropertyQueries
.<String>createQuery(getMappedClass())
.addCriteria(new NamedPropertyCriteria(idPropertyName)).getFirstResult();
} else {
this.idProperty = null;
}
if (IdentityType.class.isAssignableFrom(mappedClass) && idProperty == null) {
throw new ModelException("Id attribute not mapped to any property of [" + mappedClass + "].");
}
// Binding property is idProperty by default
if (this.bindingProperty == null) {
this.bindingProperty = this.idProperty;
}
return this;
}
public LDAPMappingConfiguration setRelatedAttributedType(Class<? extends AttributedType> relatedAttributedType) {
this.relatedAttributedType = relatedAttributedType;
return this;
}
public LDAPMappingConfiguration setParentMembershipAttributeName(String parentMembershipAttributeName) {
this.parentMembershipAttributeName = parentMembershipAttributeName;
return this;
}
public LDAPMappingConfiguration setParentMapping(Map<String, String> parentMapping) {
this.parentMapping = parentMapping;
return this;
}
public LDAPMappingConfiguration setHierarchySearchDepth(int hierarchySearchDepth) {
this.hierarchySearchDepth = hierarchySearchDepth;
return this;
}
public LDAPMappingConfiguration setBindingPropertyName(String bindingPropertyName) {
this.bindingProperty = getBindingProperty(bindingPropertyName);
return this;
}
}

View file

@ -0,0 +1,606 @@
package org.keycloak.federation.ldap.idm.store.ldap;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.PagedResultsControl;
import javax.naming.ldap.PagedResultsResponseControl;
import org.jboss.logging.Logger;
import org.keycloak.federation.ldap.idm.model.IdentityType;
import org.keycloak.federation.ldap.idm.query.IdentityQuery;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelException;
import static javax.naming.directory.SearchControls.SUBTREE_SCOPE;
/**
* <p>This class provides a set of operations to manage LDAP trees.</p>
*
* @author Anil Saldhana
* @author <a href="mailto:psilva@redhat.com">Pedro Silva</a>
*/
public class LDAPOperationManager {
private static final Logger logger = Logger.getLogger(LDAPOperationManager.class);
private final LDAPIdentityStoreConfiguration config;
private final Map<String, Object> connectionProperties;
public LDAPOperationManager(LDAPIdentityStoreConfiguration config) throws NamingException {
this.config = config;
this.connectionProperties = Collections.unmodifiableMap(createConnectionProperties());
}
/**
* <p>
* Modifies the given {@link javax.naming.directory.Attribute} instance using the given DN. This method performs a REPLACE_ATTRIBUTE
* operation.
* </p>
*
* @param dn
* @param attribute
*/
public void modifyAttribute(String dn, Attribute attribute) {
ModificationItem[] mods = new ModificationItem[]{new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attribute)};
modifyAttributes(dn, mods);
}
/**
* <p>
* Modifies the given {@link Attribute} instances using the given DN. This method performs a REPLACE_ATTRIBUTE
* operation.
* </p>
*
* @param dn
* @param attributes
*/
public void modifyAttributes(String dn, NamingEnumeration<Attribute> attributes) {
try {
List<ModificationItem> modItems = new ArrayList<ModificationItem>();
while (attributes.hasMore()) {
ModificationItem modItem = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attributes.next());
modItems.add(modItem);
}
modifyAttributes(dn, modItems.toArray(new ModificationItem[] {}));
} catch (NamingException ne) {
throw new ModelException("Could not modify attributes on entry from DN [" + dn + "]", ne);
}
}
/**
* <p>
* Removes the given {@link Attribute} instance using the given DN. This method performs a REMOVE_ATTRIBUTE
* operation.
* </p>
*
* @param dn
* @param attribute
*/
public void removeAttribute(String dn, Attribute attribute) {
ModificationItem[] mods = new ModificationItem[]{new ModificationItem(DirContext.REMOVE_ATTRIBUTE, attribute)};
modifyAttributes(dn, mods);
}
/**
* <p>
* Adds the given {@link Attribute} instance using the given DN. This method performs a ADD_ATTRIBUTE operation.
* </p>
*
* @param dn
* @param attribute
*/
public void addAttribute(String dn, Attribute attribute) {
ModificationItem[] mods = new ModificationItem[]{new ModificationItem(DirContext.ADD_ATTRIBUTE, attribute)};
modifyAttributes(dn, mods);
}
/**
* <p>
* Searches the LDAP tree.
* </p>
*
* @param baseDN
* @param id
*
* @return
*/
public void removeEntryById(final String baseDN, final String id, final LDAPMappingConfiguration mappingConfiguration) {
final String filter = getFilterById(baseDN, id);
try {
final SearchControls cons = getSearchControls(mappingConfiguration);
execute(new LdapOperation<SearchResult>() {
@Override
public SearchResult execute(LdapContext context) throws NamingException {
NamingEnumeration<SearchResult> result = context.search(baseDN, filter, cons);
if (result.hasMore()) {
SearchResult sr = result.next();
if (logger.isDebugEnabled()) {
logger.debugf("Removing entry [%s] with attributes: [", sr.getNameInNamespace());
NamingEnumeration<? extends Attribute> all = sr.getAttributes().getAll();
while (all.hasMore()) {
Attribute attribute = all.next();
logger.debugf(" %s = %s", attribute.getID(), attribute.get());
}
logger.debugf("]");
}
destroySubcontext(context, sr.getNameInNamespace());
}
result.close();
return null;
}
});
} catch (NamingException e) {
throw new ModelException("Could not remove entry from DN [" + baseDN + "] and id [" + id + "]", e);
}
}
public List<SearchResult> search(final String baseDN, final String filter, LDAPMappingConfiguration mappingConfiguration) throws NamingException {
final List<SearchResult> result = new ArrayList<SearchResult>();
final SearchControls cons = getSearchControls(mappingConfiguration);
try {
return execute(new LdapOperation<List<SearchResult>>() {
@Override
public List<SearchResult> execute(LdapContext context) throws NamingException {
NamingEnumeration<SearchResult> search = context.search(baseDN, filter, cons);
while (search.hasMoreElements()) {
result.add(search.nextElement());
}
search.close();
return result;
}
});
} catch (NamingException e) {
logger.errorf(e, "Could not query server using DN [%s] and filter [%s]", baseDN, filter);
throw e;
}
}
public <V extends IdentityType> List<SearchResult> searchPaginated(final String baseDN, final String filter, LDAPMappingConfiguration mappingConfiguration, final IdentityQuery<V> identityQuery) throws NamingException {
final List<SearchResult> result = new ArrayList<SearchResult>();
final SearchControls cons = getSearchControls(mappingConfiguration);
try {
return execute(new LdapOperation<List<SearchResult>>() {
@Override
public List<SearchResult> execute(LdapContext context) throws NamingException {
try {
byte[] cookie = (byte[])identityQuery.getPaginationContext();
PagedResultsControl pagedControls = new PagedResultsControl(identityQuery.getLimit(), cookie, Control.CRITICAL);
context.setRequestControls(new Control[] { pagedControls });
NamingEnumeration<SearchResult> search = context.search(baseDN, filter, cons);
while (search.hasMoreElements()) {
result.add(search.nextElement());
}
search.close();
Control[] responseControls = context.getResponseControls();
if (responseControls != null) {
for (Control respControl : responseControls) {
if (respControl instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc = (PagedResultsResponseControl)respControl;
cookie = prrc.getCookie();
identityQuery.setPaginationContext(cookie);
}
}
}
return result;
} catch (IOException ioe) {
logger.errorf(ioe, "Could not query server with paginated query using DN [%s], filter [%s]", baseDN, filter);
throw new NamingException(ioe.getMessage());
}
}
});
} catch (NamingException e) {
logger.errorf(e, "Could not query server using DN [%s] and filter [%s]", baseDN, filter);
throw e;
}
}
private SearchControls getSearchControls(LDAPMappingConfiguration mappingConfiguration) {
final SearchControls cons = new SearchControls();
cons.setSearchScope(SUBTREE_SCOPE);
cons.setReturningObjFlag(false);
List<String> returningAttributes = getReturningAttributes(mappingConfiguration);
cons.setReturningAttributes(returningAttributes.toArray(new String[returningAttributes.size()]));
return cons;
}
public String getFilterById(String baseDN, String id) {
String filter = null;
if (this.config.isActiveDirectory()) {
final String strObjectGUID = "<GUID=" + id + ">";
try {
Attributes attributes = execute(new LdapOperation<Attributes>() {
@Override
public Attributes execute(LdapContext context) throws NamingException {
return context.getAttributes(strObjectGUID);
}
});
byte[] objectGUID = (byte[]) attributes.get(LDAPConstants.OBJECT_GUID).get();
filter = "(&(objectClass=*)(" + getUniqueIdentifierAttributeName() + LDAPConstants.EQUAL + LDAPUtil.convertObjectGUIToByteString(objectGUID) + "))";
} catch (NamingException ne) {
return filter;
}
}
if (filter == null) {
filter = "(&(objectClass=*)(" + getUniqueIdentifierAttributeName() + LDAPConstants.EQUAL + id + "))";
}
return filter;
}
public SearchResult lookupById(final String baseDN, final String id, final LDAPMappingConfiguration mappingConfiguration) {
final String filter = getFilterById(baseDN, id);
try {
final SearchControls cons = getSearchControls(mappingConfiguration);
return execute(new LdapOperation<SearchResult>() {
@Override
public SearchResult execute(LdapContext context) throws NamingException {
NamingEnumeration<SearchResult> search = context.search(baseDN, filter, cons);
try {
if (search.hasMoreElements()) {
return search.next();
}
} finally {
if (search != null) {
search.close();
}
}
return null;
}
});
} catch (NamingException e) {
throw new ModelException("Could not query server using DN [" + baseDN + "] and filter [" + filter + "]", e);
}
}
/**
* <p>
* Destroys a subcontext with the given DN from the LDAP tree.
* </p>
*
* @param dn
*/
private void destroySubcontext(LdapContext context, final String dn) {
try {
NamingEnumeration<Binding> enumeration = null;
try {
enumeration = context.listBindings(dn);
while (enumeration.hasMore()) {
Binding binding = enumeration.next();
String name = binding.getNameInNamespace();
destroySubcontext(context, name);
}
context.unbind(dn);
} finally {
try {
enumeration.close();
} catch (Exception e) {
}
}
} catch (Exception e) {
throw new ModelException("Could not unbind DN [" + dn + "]", e);
}
}
/**
* <p>
* Performs a simple authentication using the given DN and password to bind to the authentication context.
* </p>
*
* @param dn
* @param password
*
* @return
*/
public boolean authenticate(String dn, String password) {
InitialContext authCtx = null;
try {
Hashtable<String, Object> env = new Hashtable<String, Object>(this.connectionProperties);
env.put(Context.SECURITY_PRINCIPAL, dn);
env.put(Context.SECURITY_CREDENTIALS, password);
// Never use connection pool to prevent password caching
env.put("com.sun.jndi.ldap.connect.pool", "false");
authCtx = new InitialLdapContext(env, null);
return true;
} catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debugf(e, "Authentication failed for DN [%s]", dn);
}
return false;
} finally {
if (authCtx != null) {
try {
authCtx.close();
} catch (NamingException e) {
}
}
}
}
public void modifyAttributes(final String dn, final ModificationItem[] mods) {
try {
if (logger.isDebugEnabled()) {
logger.debugf("Modifying attributes for entry [%s]: [", dn);
for (ModificationItem item : mods) {
Object values;
if (item.getAttribute().size() > 0) {
values = item.getAttribute().get();
} else {
values = "No values";
}
logger.debugf(" Op [%s]: %s = %s", item.getModificationOp(), item.getAttribute().getID(), values);
}
logger.debugf("]");
}
execute(new LdapOperation<Void>() {
@Override
public Void execute(LdapContext context) throws NamingException {
context.modifyAttributes(dn, mods);
return null;
}
});
} catch (NamingException e) {
throw new ModelException("Could not modify attribute for DN [" + dn + "]", e);
}
}
public void createSubContext(final String name, final Attributes attributes) {
try {
if (logger.isDebugEnabled()) {
logger.debugf("Creating entry [%s] with attributes: [", name);
NamingEnumeration<? extends Attribute> all = attributes.getAll();
while (all.hasMore()) {
Attribute attribute = all.next();
logger.debugf(" %s = %s", attribute.getID(), attribute.get());
}
logger.debugf("]");
}
execute(new LdapOperation<Void>() {
@Override
public Void execute(LdapContext context) throws NamingException {
DirContext subcontext = context.createSubcontext(name, attributes);
subcontext.close();
return null;
}
});
} catch (NamingException e) {
throw new ModelException("Error creating subcontext [" + name + "]", e);
}
}
private String getUniqueIdentifierAttributeName() {
return this.config.getUniqueIdentifierAttributeName();
}
private NamingEnumeration<SearchResult> createEmptyEnumeration() {
return new NamingEnumeration<SearchResult>() {
@Override
public SearchResult next() throws NamingException {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public boolean hasMore() throws NamingException {
return false; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void close() throws NamingException {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public boolean hasMoreElements() {
return false; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public SearchResult nextElement() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
};
}
public Attributes getAttributes(final String entryUUID, final String baseDN, LDAPMappingConfiguration mappingConfiguration) {
SearchResult search = lookupById(baseDN, entryUUID, mappingConfiguration);
if (search == null) {
throw new ModelException("Couldn't find item with entryUUID [" + entryUUID + "] and baseDN [" + baseDN + "]");
}
return search.getAttributes();
}
public String decodeEntryUUID(final Object entryUUID) {
String id;
if (this.config.isActiveDirectory()) {
id = LDAPUtil.decodeObjectGUID((byte[]) entryUUID);
} else {
id = entryUUID.toString();
}
return id;
}
private LdapContext createLdapContext() throws NamingException {
return new InitialLdapContext(new Hashtable<Object, Object>(this.connectionProperties), null);
}
private Map<String, Object> createConnectionProperties() {
HashMap<String, Object> env = new HashMap<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY, this.config.getFactoryName());
env.put(Context.SECURITY_AUTHENTICATION, this.config.getAuthType());
String protocol = this.config.getProtocol();
if (protocol != null) {
env.put(Context.SECURITY_PROTOCOL, protocol);
}
String bindDN = this.config.getBindDN();
char[] bindCredential = null;
if (this.config.getBindCredential() != null) {
bindCredential = this.config.getBindCredential().toCharArray();
}
if (bindDN != null) {
env.put(Context.SECURITY_PRINCIPAL, bindDN);
env.put(Context.SECURITY_CREDENTIALS, bindCredential);
}
String url = this.config.getLdapURL();
if (url == null) {
throw new RuntimeException("url");
}
env.put(Context.PROVIDER_URL, url);
// Just dump the additional properties
Properties additionalProperties = this.config.getConnectionProperties();
if (additionalProperties != null) {
for (Object key : additionalProperties.keySet()) {
env.put(key.toString(), additionalProperties.getProperty(key.toString()));
}
}
if (config.isActiveDirectory()) {
env.put("java.naming.ldap.attributes.binary", LDAPConstants.OBJECT_GUID);
}
if (logger.isDebugEnabled()) {
logger.debugf("Creating LdapContext using properties: [%s]", env);
}
return env;
}
private <R> R execute(LdapOperation<R> operation) throws NamingException {
LdapContext context = null;
try {
context = createLdapContext();
return operation.execute(context);
} catch (NamingException ne) {
logger.error("Could not create Ldap context or operation execution error.", ne);
throw ne;
} finally {
if (context != null) {
try {
context.close();
} catch (NamingException ne) {
logger.error("Could not close Ldap context.", ne);
}
}
}
}
private interface LdapOperation<R> {
R execute(LdapContext context) throws NamingException;
}
private List<String> getReturningAttributes(final LDAPMappingConfiguration mappingConfiguration) {
List<String> returningAttributes = new ArrayList<String>();
if (mappingConfiguration != null) {
returningAttributes.addAll(mappingConfiguration.getMappedProperties().values());
returningAttributes.add(mappingConfiguration.getParentMembershipAttributeName());
// for (LDAPMappingConfiguration relationshipConfig : this.config.getRelationshipConfigs()) {
// if (relationshipConfig.getRelatedAttributedType().equals(mappingConfiguration.getMappedClass())) {
// returningAttributes.addAll(relationshipConfig.getMappedProperties().values());
// }
// }
} else {
returningAttributes.add("*");
}
returningAttributes.add(getUniqueIdentifierAttributeName());
returningAttributes.add(LDAPConstants.CREATE_TIMESTAMP);
returningAttributes.add(LDAPConstants.OBJECT_CLASS);
return returningAttributes;
}
}

View file

@ -0,0 +1,158 @@
package org.keycloak.federation.ldap.idm.store.ldap;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import org.keycloak.models.ModelException;
/**
* <p>Utility class for working with LDAP.</p>
*
* @author Pedro Igor
*/
public class LDAPUtil {
/**
* <p>Formats the given date.</p>
*
* @param date The Date to format.
*
* @return A String representing the formatted date.
*/
public static final String formatDate(Date date) {
if (date == null) {
throw new IllegalArgumentException("You must provide a date.");
}
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss'.0Z'");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return dateFormat.format(date);
}
/**
* <p>
* Parses dates/time stamps stored in LDAP. Some possible values:
* </p>
* <ul>
* <li>20020228150820</li>
* <li>20030228150820Z</li>
* <li>20050228150820.12</li>
* <li>20060711011740.0Z</li>
* </ul>
*
* @param date The date string to parse from.
*
* @return the Date.
*/
public static final Date parseDate(String date) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
try {
if (date.endsWith("Z")) {
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
} else {
dateFormat.setTimeZone(TimeZone.getDefault());
}
return dateFormat.parse(date);
} catch (Exception e) {
throw new ModelException("Error converting ldap date.", e);
}
}
/**
* <p>Creates a byte-based {@link String} representation of a raw byte array representing the value of the
* <code>objectGUID</code> attribute retrieved from Active Directory.</p>
*
* <p>The returned string is useful to perform queries on AD based on the <code>objectGUID</code> value. Eg.:</p>
*
* <p>
* String filter = "(&(objectClass=*)(objectGUID" + EQUAL + convertObjectGUIToByteString(objectGUID) + "))";
* </p>
*
* @param objectGUID A raw byte array representing the value of the <code>objectGUID</code> attribute retrieved from
* Active Directory.
*
* @return A byte-based String representation in the form of \[0]\[1]\[2]\[3]\[4]\[5]\[6]\[7]\[8]\[9]\[10]\[11]\[12]\[13]\[14]\[15]
*/
public static String convertObjectGUIToByteString(byte[] objectGUID) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < objectGUID.length; i++) {
String transformed = prefixZeros((int) objectGUID[i] & 0xFF);
result.append("\\");
result.append(transformed);
}
return result.toString();
}
/**
* <p>Decode a raw byte array representing the value of the <code>objectGUID</code> attribute retrieved from Active
* Directory.</p>
*
* <p>The returned string is useful to directly bind an entry. Eg.:</p>
*
* <p>
* String bindingString = decodeObjectGUID(objectGUID);
* <br/>
* Attributes attributes = ctx.getAttributes(bindingString);
* </p>
*
* @param objectGUID A raw byte array representing the value of the <code>objectGUID</code> attribute retrieved from
* Active Directory.
*
* @return A string representing the decoded value in the form of [3][2][1][0]-[5][4]-[7][6]-[8][9]-[10][11][12][13][14][15].
*/
public static String decodeObjectGUID(byte[] objectGUID) {
StringBuilder displayStr = new StringBuilder();
displayStr.append(convertToDashedString(objectGUID));
return displayStr.toString();
}
private static String convertToDashedString(byte[] objectGUID) {
StringBuilder displayStr = new StringBuilder();
displayStr.append(prefixZeros((int) objectGUID[3] & 0xFF));
displayStr.append(prefixZeros((int) objectGUID[2] & 0xFF));
displayStr.append(prefixZeros((int) objectGUID[1] & 0xFF));
displayStr.append(prefixZeros((int) objectGUID[0] & 0xFF));
displayStr.append("-");
displayStr.append(prefixZeros((int) objectGUID[5] & 0xFF));
displayStr.append(prefixZeros((int) objectGUID[4] & 0xFF));
displayStr.append("-");
displayStr.append(prefixZeros((int) objectGUID[7] & 0xFF));
displayStr.append(prefixZeros((int) objectGUID[6] & 0xFF));
displayStr.append("-");
displayStr.append(prefixZeros((int) objectGUID[8] & 0xFF));
displayStr.append(prefixZeros((int) objectGUID[9] & 0xFF));
displayStr.append("-");
displayStr.append(prefixZeros((int) objectGUID[10] & 0xFF));
displayStr.append(prefixZeros((int) objectGUID[11] & 0xFF));
displayStr.append(prefixZeros((int) objectGUID[12] & 0xFF));
displayStr.append(prefixZeros((int) objectGUID[13] & 0xFF));
displayStr.append(prefixZeros((int) objectGUID[14] & 0xFF));
displayStr.append(prefixZeros((int) objectGUID[15] & 0xFF));
return displayStr.toString();
}
private static String prefixZeros(int value) {
if (value <= 0xF) {
StringBuilder sb = new StringBuilder("0");
sb.append(Integer.toHexString(value));
return sb.toString();
} else {
return Integer.toHexString(value);
}
}
}

View file

@ -1,14 +1,11 @@
package org.keycloak.account.freemarker.model;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.util.Time;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
@ -63,23 +60,14 @@ public class SessionsBean {
return Time.toDate(max);
}
public Set<String> getApplications() {
Set<String> apps = new HashSet<String>();
public Set<String> getClients() {
Set<String> clients = new HashSet<String>();
for (ClientSessionModel clientSession : session.getClientSessions()) {
ClientModel client = clientSession.getClient();
if (client instanceof ApplicationModel) apps.add(client.getClientId());
clients.add(client.getClientId());
}
return apps;
return clients;
}
public List<String> getClients() {
List<String> apps = new ArrayList<String>();
for (ClientSessionModel clientSession : session.getClientSessions()) {
ClientModel client = clientSession.getClient();
if (client instanceof OAuthClientModel) apps.add(client.getClientId());
}
return apps;
}
}
}

View file

@ -14,7 +14,6 @@
<td>${msg("started")}</td>
<td>${msg("lastAccess")}</td>
<td>${msg("expires")}</td>
<td>${msg("applications")}</td>
<td>${msg("clients")}</td>
</tr>
</thead>
@ -26,11 +25,6 @@
<td>${session.started?datetime}</td>
<td>${session.lastAccess?datetime}</td>
<td>${session.expires?datetime}</td>
<td>
<#list session.applications as app>
${app}<br/>
</#list>
</td>
<td>
<#list session.clients as client>
${client}<br/>

View file

@ -30,8 +30,7 @@
<script src="${resourceUrl}/js/app.js" type="text/javascript"></script>
<script src="${resourceUrl}/js/controllers/realm.js" type="text/javascript"></script>
<script src="${resourceUrl}/js/controllers/applications.js" type="text/javascript"></script>
<script src="${resourceUrl}/js/controllers/oauth-clients.js" type="text/javascript"></script>
<script src="${resourceUrl}/js/controllers/clients.js" type="text/javascript"></script>
<script src="${resourceUrl}/js/controllers/users.js" type="text/javascript"></script>
<script src="${resourceUrl}/js/loaders.js" type="text/javascript"></script>
<script src="${resourceUrl}/js/services.js" type="text/javascript"></script>

View file

@ -212,8 +212,8 @@ module.config([ '$routeProvider', function($routeProvider) {
realm : function(RealmLoader) {
return RealmLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
clients : function(ClientListLoader) {
return ClientListLoader();
},
roles : function(RoleListLoader) {
return RoleListLoader();
@ -311,8 +311,8 @@ module.config([ '$routeProvider', function($routeProvider) {
user : function(UserLoader) {
return UserLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
clients : function(ClientListLoader) {
return ClientListLoader();
}
},
controller : 'UserRoleMappingCtrl'
@ -369,8 +369,8 @@ module.config([ '$routeProvider', function($routeProvider) {
roles : function(RoleListLoader) {
return RoleListLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
clients : function(ClientListLoader) {
return ClientListLoader();
}
},
controller : 'RoleDetailCtrl'
@ -387,8 +387,8 @@ module.config([ '$routeProvider', function($routeProvider) {
roles : function(RoleListLoader) {
return RoleListLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
clients : function(ClientListLoader) {
return ClientListLoader();
}
},
controller : 'RoleDetailCtrl'
@ -406,14 +406,14 @@ module.config([ '$routeProvider', function($routeProvider) {
controller : 'RoleListCtrl'
})
.when('/create/role/:realm/applications/:application', {
templateUrl : resourceUrl + '/partials/application-role-detail.html',
.when('/create/role/:realm/clients/:client', {
templateUrl : resourceUrl + '/partials/client-role-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
},
role : function() {
return {};
@ -421,56 +421,56 @@ module.config([ '$routeProvider', function($routeProvider) {
roles : function(RoleListLoader) {
return RoleListLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
clients : function(ClientListLoader) {
return ClientListLoader();
}
},
controller : 'ApplicationRoleDetailCtrl'
controller : 'ClientRoleDetailCtrl'
})
.when('/realms/:realm/applications/:application/roles/:role', {
templateUrl : resourceUrl + '/partials/application-role-detail.html',
.when('/realms/:realm/clients/:client/roles/:role', {
templateUrl : resourceUrl + '/partials/client-role-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
},
role : function(ApplicationRoleLoader) {
return ApplicationRoleLoader();
role : function(ClientRoleLoader) {
return ClientRoleLoader();
},
roles : function(RoleListLoader) {
return RoleListLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
clients : function(ClientListLoader) {
return ClientListLoader();
}
},
controller : 'ApplicationRoleDetailCtrl'
controller : 'ClientRoleDetailCtrl'
})
.when('/realms/:realm/applications/:application/mappers', {
templateUrl : resourceUrl + '/partials/application-mappers.html',
.when('/realms/:realm/clients/:client/mappers', {
templateUrl : resourceUrl + '/partials/client-mappers.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
controller : 'ApplicationProtocolMapperListCtrl'
controller : 'ClientProtocolMapperListCtrl'
})
.when('/realms/:realm/applications/:application/add-mappers', {
templateUrl : resourceUrl + '/partials/application-mappers-add.html',
.when('/realms/:realm/clients/:client/add-mappers', {
templateUrl : resourceUrl + '/partials/client-mappers-add.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
@ -478,26 +478,26 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'AddBuiltinProtocolMapperCtrl'
})
.when('/realms/:realm/applications/:application/mappers/:id', {
.when('/realms/:realm/clients/:client/mappers/:id', {
templateUrl : resourceUrl + '/partials/protocol-mapper-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
},
mapper : function(ApplicationProtocolMapperLoader) {
return ApplicationProtocolMapperLoader();
mapper : function(ClientProtocolMapperLoader) {
return ClientProtocolMapperLoader();
}
},
controller : 'ApplicationProtocolMapperCtrl'
controller : 'ClientProtocolMapperCtrl'
})
.when('/create/application/:realm/:application/mappers', {
.when('/create/client/:realm/:client/mappers', {
templateUrl : resourceUrl + '/partials/protocol-mapper-detail.html',
resolve : {
realm : function(RealmLoader) {
@ -506,297 +506,231 @@ module.config([ '$routeProvider', function($routeProvider) {
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
}
},
controller : 'ApplicationProtocolMapperCreateCtrl'
controller : 'ClientProtocolMapperCreateCtrl'
})
.when('/realms/:realm/oauth-clients/:oauth/mappers', {
templateUrl : resourceUrl + '/partials/oauth-client-mappers.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
controller : 'OAuthClientProtocolMapperListCtrl'
})
.when('/realms/:realm/oauth-clients/:oauth/add-mappers', {
templateUrl : resourceUrl + '/partials/oauth-client-mappers-add.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
controller : 'OAuthClientAddBuiltinProtocolMapperCtrl'
})
.when('/realms/:realm/oauth-clients/:oauth/mappers/:id', {
templateUrl : resourceUrl + '/partials/oauth-client-protocol-mapper-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
},
mapper : function(OAuthClientProtocolMapperLoader) {
return OAuthClientProtocolMapperLoader();
}
},
controller : 'OAuthClientProtocolMapperCtrl'
})
.when('/create/oauth-client/:realm/:oauth/mappers', {
templateUrl : resourceUrl + '/partials/oauth-client-protocol-mapper-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
}
},
controller : 'OAuthClientProtocolMapperCreateCtrl'
})
.when('/realms/:realm/applications/:application/sessions', {
templateUrl : resourceUrl + '/partials/application-sessions.html',
.when('/realms/:realm/clients/:client/sessions', {
templateUrl : resourceUrl + '/partials/client-sessions.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
},
sessionCount : function(ApplicationSessionCountLoader) {
return ApplicationSessionCountLoader();
sessionCount : function(ClientSessionCountLoader) {
return ClientSessionCountLoader();
}
},
controller : 'ApplicationSessionsCtrl'
controller : 'ClientSessionsCtrl'
})
.when('/realms/:realm/applications/:application/credentials', {
templateUrl : resourceUrl + '/partials/application-credentials.html',
.when('/realms/:realm/clients/:client/credentials', {
templateUrl : resourceUrl + '/partials/client-credentials.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
}
},
controller : 'ApplicationCredentialsCtrl'
controller : 'ClientCredentialsCtrl'
})
.when('/realms/:realm/applications/:application/identity-provider', {
templateUrl : resourceUrl + '/partials/application-identity-provider.html',
.when('/realms/:realm/clients/:client/identity-provider', {
templateUrl : resourceUrl + '/partials/client-identity-provider.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
}
},
controller : 'ApplicationIdentityProviderCtrl'
controller : 'ClientIdentityProviderCtrl'
})
.when('/realms/:realm/applications/:application/clustering', {
templateUrl : resourceUrl + '/partials/application-clustering.html',
.when('/realms/:realm/clients/:client/clustering', {
templateUrl : resourceUrl + '/partials/client-clustering.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
}
},
controller : 'ApplicationClusteringCtrl'
controller : 'ClientClusteringCtrl'
})
.when('/register-node/realms/:realm/applications/:application/clustering', {
templateUrl : resourceUrl + '/partials/application-clustering-node.html',
.when('/register-node/realms/:realm/clients/:client/clustering', {
templateUrl : resourceUrl + '/partials/client-clustering-node.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
}
},
controller : 'ApplicationClusteringNodeCtrl'
controller : 'ClientClusteringNodeCtrl'
})
.when('/realms/:realm/applications/:application/clustering/:node', {
templateUrl : resourceUrl + '/partials/application-clustering-node.html',
.when('/realms/:realm/clients/:client/clustering/:node', {
templateUrl : resourceUrl + '/partials/client-clustering-node.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
}
},
controller : 'ApplicationClusteringNodeCtrl'
controller : 'ClientClusteringNodeCtrl'
})
.when('/realms/:realm/applications/:application/saml/keys', {
templateUrl : resourceUrl + '/partials/application-saml-keys.html',
.when('/realms/:realm/clients/:client/saml/keys', {
templateUrl : resourceUrl + '/partials/client-saml-keys.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
}
},
controller : 'ApplicationSamlKeyCtrl'
controller : 'ClientSamlKeyCtrl'
})
.when('/realms/:realm/applications/:application/saml/:keyType/import/:attribute', {
templateUrl : resourceUrl + '/partials/application-saml-key-import.html',
.when('/realms/:realm/clients/:client/saml/:keyType/import/:attribute', {
templateUrl : resourceUrl + '/partials/client-saml-key-import.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
}
},
controller : 'ApplicationCertificateImportCtrl'
controller : 'ClientCertificateImportCtrl'
})
.when('/realms/:realm/applications/:application/saml/:keyType/export/:attribute', {
templateUrl : resourceUrl + '/partials/application-saml-key-export.html',
.when('/realms/:realm/clients/:client/saml/:keyType/export/:attribute', {
templateUrl : resourceUrl + '/partials/client-saml-key-export.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
}
},
controller : 'ApplicationCertificateExportCtrl'
controller : 'ClientCertificateExportCtrl'
})
.when('/realms/:realm/applications/:application/roles', {
templateUrl : resourceUrl + '/partials/application-role-list.html',
.when('/realms/:realm/clients/:client/roles', {
templateUrl : resourceUrl + '/partials/client-role-list.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
},
roles : function(ApplicationRoleListLoader) {
return ApplicationRoleListLoader();
roles : function(ClientRoleListLoader) {
return ClientRoleListLoader();
}
},
controller : 'ApplicationRoleListCtrl'
controller : 'ClientRoleListCtrl'
})
.when('/realms/:realm/applications/:application/revocation', {
templateUrl : resourceUrl + '/partials/application-revocation.html',
.when('/realms/:realm/clients/:client/revocation', {
templateUrl : resourceUrl + '/partials/client-revocation.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
}
},
controller : 'ApplicationRevocationCtrl'
controller : 'ClientRevocationCtrl'
})
.when('/realms/:realm/applications/:application/scope-mappings', {
templateUrl : resourceUrl + '/partials/application-scope-mappings.html',
.when('/realms/:realm/clients/:client/scope-mappings', {
templateUrl : resourceUrl + '/partials/client-scope-mappings.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
clients : function(ClientListLoader) {
return ClientListLoader();
}
},
controller : 'ApplicationScopeMappingCtrl'
controller : 'ClientScopeMappingCtrl'
})
.when('/realms/:realm/applications/:application/installation', {
templateUrl : resourceUrl + '/partials/application-installation.html',
.when('/realms/:realm/clients/:client/installation', {
templateUrl : resourceUrl + '/partials/client-installation.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
}
},
controller : 'ApplicationInstallationCtrl'
controller : 'ClientInstallationCtrl'
})
.when('/create/application/:realm', {
templateUrl : resourceUrl + '/partials/application-detail.html',
.when('/create/client/:realm', {
templateUrl : resourceUrl + '/partials/client-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
clients : function(ClientListLoader) {
return ClientListLoader();
},
application : function() {
client : function() {
return {};
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
controller : 'ApplicationDetailCtrl'
controller : 'ClientDetailCtrl'
})
.when('/realms/:realm/applications/:application', {
templateUrl : resourceUrl + '/partials/application-detail.html',
.when('/realms/:realm/clients/:client', {
templateUrl : resourceUrl + '/partials/client-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
clients : function(ClientListLoader) {
return ClientListLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
client : function(ClientLoader) {
return ClientLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
controller : 'ApplicationDetailCtrl'
controller : 'ClientDetailCtrl'
})
.when('/realms/:realm/applications', {
templateUrl : resourceUrl + '/partials/application-list.html',
.when('/realms/:realm/clients', {
templateUrl : resourceUrl + '/partials/client-list.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
clients : function(ClientListLoader) {
return ClientListLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
controller : 'ApplicationListCtrl'
controller : 'ClientListCtrl'
})
.when('/import/application/:realm', {
templateUrl : resourceUrl + '/partials/application-import.html',
.when('/import/client/:realm', {
templateUrl : resourceUrl + '/partials/client-import.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
@ -805,129 +739,8 @@ module.config([ '$routeProvider', function($routeProvider) {
return ServerInfoLoader();
}
},
controller : 'ApplicationImportCtrl'
controller : 'ClientImportCtrl'
})
// OAUTH Client
.when('/realms/:realm/oauth-clients/:oauth/claims', {
templateUrl : resourceUrl + '/partials/oauth-client-claims.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
},
claims : function(OAuthClientClaimsLoader) {
return OAuthClientClaimsLoader();
}
},
controller : 'OAuthClientClaimsCtrl'
})
.when('/realms/:realm/oauth-clients/:oauth/revocation', {
templateUrl : resourceUrl + '/partials/oauth-client-revocation.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
}
},
controller : 'OAuthClientRevocationCtrl'
})
.when('/realms/:realm/oauth-clients/:oauth/credentials', {
templateUrl : resourceUrl + '/partials/oauth-client-credentials.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
}
},
controller : 'OAuthClientCredentialsCtrl'
})
.when('/realms/:realm/oauth-clients/:oauth/scope-mappings', {
templateUrl : resourceUrl + '/partials/oauth-client-scope-mappings.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
}
},
controller : 'OAuthClientScopeMappingCtrl'
})
.when('/realms/:realm/oauth-clients/:oauth/installation', {
templateUrl : resourceUrl + '/partials/oauth-client-installation.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
},
installation : function(OAuthClientInstallationLoader) {
return OAuthClientInstallationLoader();
}
},
controller : 'OAuthClientInstallationCtrl'
})
.when('/create/oauth-client/:realm', {
templateUrl : resourceUrl + '/partials/oauth-client-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function() {
return {};
}
},
controller : 'OAuthClientDetailCtrl'
})
.when('/realms/:realm/oauth-clients/:oauth', {
templateUrl : resourceUrl + '/partials/oauth-client-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
}
},
controller : 'OAuthClientDetailCtrl'
})
.when('/realms/:realm/oauth-clients/:oauth/identity-provider', {
templateUrl : resourceUrl + '/partials/oauth-client-identity-provider.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
}
},
controller : 'OAuthClientIdentityProviderCtrl'
})
.when('/realms/:realm/oauth-clients', {
templateUrl : resourceUrl + '/partials/oauth-client-list.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauthClients : function(OAuthClientListLoader) {
return OAuthClientListLoader();
}
},
controller : 'OAuthClientListCtrl'
})
.when('/', {
templateUrl : resourceUrl + '/partials/home.html',
controller : 'HomeCtrl'
@ -959,8 +772,8 @@ module.config([ '$routeProvider', function($routeProvider) {
realm : function(RealmLoader) {
return RealmLoader();
},
stats : function(RealmApplicationSessionStatsLoader) {
return RealmApplicationSessionStatsLoader();
stats : function(RealmClientSessionStatsLoader) {
return RealmClientSessionStatsLoader();
}
},
controller : 'RealmSessionStatsCtrl'
@ -1540,21 +1353,12 @@ module.directive('kcNavigation', function ($compile, Notifications) {
}
});
module.directive('kcNavigationApplication', function () {
module.directive('kcNavigationClient', function () {
return {
scope: true,
restrict: 'E',
replace: true,
templateUrl: resourceUrl + '/templates/kc-navigation-application.html'
}
});
module.directive('kcNavigationOauthClient', function () {
return {
scope: true,
restrict: 'E',
replace: true,
templateUrl: resourceUrl + '/templates/kc-navigation-oauth-client.html'
templateUrl: resourceUrl + '/templates/kc-navigation-client.html'
}
});

View file

@ -1,611 +0,0 @@
module.controller('OAuthClientClaimsCtrl', function($scope, realm, oauth, claims,
OAuthClientClaims,
$location, Dialog, Notifications) {
$scope.realm = realm;
$scope.oauth = oauth;
$scope.claims = angular.copy(claims);
$scope.changed = false;
$scope.$watch('claims', function () {
if (!angular.equals($scope.claims, claims)) {
$scope.changed = true;
}
}, true);
$scope.save = function () {
OAuthClientClaims.update({
realm: realm.realm,
oauth: oauth.id
}, $scope.claims, function () {
$scope.changed = false;
claims = angular.copy($scope.claims);
Notifications.success("Your claim changes have been saved.");
});
};
$scope.reset = function () {
$location.url("/realms/" + realm.realm + "/oauth-clients/" + oauth.id + "/claims");
};
});
module.controller('OAuthClientCredentialsCtrl', function($scope, $location, realm, oauth, OAuthClientCredentials, Notifications) {
$scope.realm = realm;
$scope.oauth = oauth;
var secret = OAuthClientCredentials.get({ realm : realm.realm, oauth : oauth.id },
function() {
$scope.secret = secret.value;
}
);
$scope.changePassword = function() {
var secret = OAuthClientCredentials.update({ realm : realm.realm, oauth : oauth.id },
function() {
Notifications.success('The secret has been changed.');
$scope.secret = secret.value;
},
function() {
Notifications.error("The secret was not changed due to a problem.");
$scope.secret = "error";
}
);
};
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
});
module.controller('OAuthClientListCtrl', function($scope, realm, oauthClients, OAuthClient, $location) {
$scope.realm = realm;
$scope.oauthClients = oauthClients;
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
});
module.controller('OAuthClientDetailCtrl', function($scope, realm, oauth, OAuthClient, $location, Dialog, Notifications) {
$scope.realm = realm;
$scope.create = !oauth.id;
$scope.accessTypes = [
"confidential",
"public"
];
$scope.changeAccessType = function() {
if ($scope.accessType == "confidential") {
$scope.oauth.publicClient = false;
} else if ($scope.accessType == "public") {
$scope.oauth.publicClient = true;
}
};
if (!$scope.create) {
$scope.oauth= angular.copy(oauth);
$scope.accessType = $scope.accessTypes[0];
if (oauth.publicClient) {
$scope.accessType = $scope.accessTypes[1];
}
} else {
$scope.oauth = { enabled: true };
$scope.oauth.webOrigins = [];
$scope.oauth.redirectUris = [];
$scope.accessType = $scope.accessTypes[0];
}
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
$scope.$watch('oauth', function() {
if (!angular.equals($scope.oauth, oauth)) {
$scope.changed = true;
}
}, true);
$scope.deleteWebOrigin = function(index) {
$scope.oauth.webOrigins.splice(index, 1);
}
$scope.addWebOrigin = function() {
$scope.oauth.webOrigins.push($scope.newWebOrigin);
$scope.newWebOrigin = "";
}
$scope.deleteRedirectUri = function(index) {
$scope.oauth.redirectUris.splice(index, 1);
}
$scope.addRedirectUri = function() {
$scope.oauth.redirectUris.push($scope.newRedirectUri);
$scope.newRedirectUri = "";
}
$scope.save = function() {
if (!$scope.oauth.directGrantsOnly && (!$scope.oauth.redirectUris || $scope.oauth.redirectUris.length == 0)) {
Notifications.error("You must specify at least one redirect uri");
} else {
if ($scope.create) {
OAuthClient.save({
realm: realm.realm
}, $scope.oauth, function (data, headers) {
$scope.changed = false;
var l = headers().location;
var name = l.substring(l.lastIndexOf("/") + 1);
$location.url("/realms/" + realm.realm + "/oauth-clients/" + name);
Notifications.success("The oauth client has been created.");
});
} else {
OAuthClient.update({
realm : realm.realm,
oauth : oauth.id
}, $scope.oauth, function() {
$scope.changed = false;
oauth = angular.copy($scope.oauth);
$location.url("/realms/" + realm.realm + "/oauth-clients/" + oauth.id);
Notifications.success("Your changes have been saved to the oauth client.");
});
}
}
};
$scope.reset = function() {
$scope.oauth = angular.copy(oauth);
$scope.changed = false;
};
$scope.cancel = function() {
$location.url("/realms/" + realm.realm + "/oauth-clients");
};
$scope.remove = function() {
Dialog.confirmDelete($scope.oauth.id, 'oauth', function() {
$scope.oauth.$remove({
realm : realm.realm,
oauth : $scope.oauth.id
}, function() {
$location.url("/realms/" + realm.realm + "/oauth-clients");
Notifications.success("The oauth client has been deleted.");
});
});
};
});
module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm, oauth, applications, Notifications,
OAuthClient,
OAuthClientRealmScopeMapping, OAuthClientApplicationScopeMapping, ApplicationRole,
OAuthClientAvailableRealmScopeMapping, OAuthClientAvailableApplicationScopeMapping,
OAuthClientCompositeRealmScopeMapping, OAuthClientCompositeApplicationScopeMapping) {
$scope.realm = realm;
$scope.oauth = angular.copy(oauth);
$scope.selectedRealmRoles = [];
$scope.selectedRealmMappings = [];
$scope.realmMappings = [];
$scope.applications = applications;
$scope.applicationRoles = [];
$scope.applicationComposite = [];
$scope.selectedApplicationRoles = [];
$scope.selectedApplicationMappings = [];
$scope.applicationMappings = [];
$scope.dummymodel = [];
$scope.changeFullScopeAllowed = function() {
console.log('change full scope');
OAuthClient.update({
realm : realm.realm,
oauth : oauth.id
}, $scope.oauth, function() {
$scope.changed = false;
oauth = angular.copy($scope.oauth);
Notifications.success("Scope mappings updated.");
});
}
function updateRealmRoles() {
$scope.realmRoles = OAuthClientAvailableRealmScopeMapping.query({realm : realm.realm, oauth : oauth.id});
$scope.realmMappings = OAuthClientRealmScopeMapping.query({realm : realm.realm, oauth : oauth.id});
$scope.realmComposite = OAuthClientCompositeRealmScopeMapping.query({realm : realm.realm, oauth : oauth.id});
}
function updateAppRoles() {
if ($scope.targetApp) {
console.debug($scope.targetApp.name);
$scope.applicationRoles = OAuthClientAvailableApplicationScopeMapping.query({realm : realm.realm, oauth : oauth.id, targetApp : $scope.targetApp.id});
$scope.applicationMappings = OAuthClientApplicationScopeMapping.query({realm : realm.realm, oauth : oauth.id, targetApp : $scope.targetApp.id});
$scope.applicationComposite = OAuthClientCompositeApplicationScopeMapping.query({realm : realm.realm, oauth : oauth.id, targetApp : $scope.targetApp.id});
} else {
$scope.applicationRoles = null;
$scope.applicationMappings = null;
$scope.applicationComposite = null;
}
}
$scope.changeApplication = function() {
updateAppRoles();
};
$scope.addRealmRole = function() {
$http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients-by-id/' + oauth.id + '/scope-mappings/realm',
$scope.selectedRealmRoles).success(function () {
updateRealmRoles();
Notifications.success("Scope mappings updated.");
});
};
$scope.deleteRealmRole = function() {
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients-by-id/' + oauth.id + '/scope-mappings/realm',
{data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(function () {
updateRealmRoles();
Notifications.success("Scope mappings updated.");
});
};
$scope.addApplicationRole = function() {
$http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients-by-id/' + oauth.id + '/scope-mappings/applications-by-id/' + $scope.targetApp.id,
$scope.selectedApplicationRoles).success(function () {
updateAppRoles();
Notifications.success("Scope mappings updated.");
});
};
$scope.deleteApplicationRole = function() {
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients-by-id/' + oauth.id + '/scope-mappings/applications-by-id/' + $scope.targetApp.id,
{data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(function () {
updateAppRoles();
Notifications.success("Scope mappings updated.");
});
};
updateRealmRoles();
});
module.controller('OAuthClientInstallationCtrl', function($scope, realm, installation, oauth, OAuthClientInstallation, $routeParams) {
$scope.realm = realm;
$scope.oauth = oauth;
$scope.installation = installation;
$scope.download = function() {
saveAs(new Blob([angular.toJson($scope.installation, true)], { type: 'application/json' }), 'keycloak.json');
}
});
module.controller('OAuthClientRevocationCtrl', function($scope, realm, oauth, OAuthClient, $location, Dialog, Notifications) {
$scope.oauth = oauth;
$scope.realm = realm;
var setNotBefore = function() {
if ($scope.oauth.notBefore == 0) {
$scope.notBefore = "None";
} else {
$scope.notBefore = new Date($scope.oauth.notBefore * 1000);
}
};
setNotBefore();
var refresh = function() {
OAuthClient.get({ realm : realm.realm, oauth: $scope.oauth.id }, function(updated) {
$scope.oauth = updated;
setNotBefore();
})
};
$scope.clear = function() {
$scope.oauth.notBefore = 0;
OAuthClient.update({ realm : realm.realm, oauth: $scope.oauth.id}, $scope.oauth, function () {
$scope.notBefore = "None";
Notifications.success('Not Before cleared for application.');
refresh();
});
}
$scope.setNotBeforeNow = function() {
$scope.oauth.notBefore = new Date().getTime()/1000;
OAuthClient.update({ realm : realm.realm, oauth: $scope.oauth.id}, $scope.oauth, function () {
Notifications.success('Not Before cleared for application.');
refresh();
});
}
});
module.controller('OAuthClientIdentityProviderCtrl', function($scope, $route, realm, oauth, OAuthClient, $location, Notifications) {
$scope.realm = realm;
$scope.oauth = angular.copy(oauth);
var length = 0;
if ($scope.oauth.identityProviders) {
length = $scope.oauth.identityProviders.length;
} else {
$scope.oauth.identityProviders = new Array(realm.identityProviders.length);
}
for (j = length; j < realm.identityProviders.length; j++) {
$scope.oauth.identityProviders[j] = {};
}
$scope.identityProviders = [];
for (j = 0; j < realm.identityProviders.length; j++) {
var identityProvider = realm.identityProviders[j];
var match = false;
var applicationProvider;
for (i = 0; i < $scope.oauth.identityProviders.length; i++) {
applicationProvider = $scope.oauth.identityProviders[i];
if (applicationProvider) {
if (applicationProvider.retrieveToken) {
applicationProvider.retrieveToken = applicationProvider.retrieveToken.toString();
} else {
applicationProvider.retrieveToken = false.toString();
}
if (applicationProvider.id == identityProvider.id) {
$scope.identityProviders[i] = {};
$scope.identityProviders[i].identityProvider = identityProvider;
$scope.identityProviders[i].retrieveToken = applicationProvider.retrieveToken.toString();
break;
}
applicationProvider = null;
}
}
if (applicationProvider == null) {
var length = $scope.identityProviders.length + $scope.oauth.identityProviders.length;
$scope.identityProviders[length] = {};
$scope.identityProviders[length].identityProvider = identityProvider;
$scope.identityProviders[length].retrieveToken = false.toString();
}
}
$scope.identityProviders = $scope.identityProviders.filter(function(n){ return n != undefined });
var oldCopy = angular.copy($scope.oauth);
$scope.save = function() {
var selectedProviders = [];
for (i = 0; i < $scope.oauth.identityProviders.length; i++) {
var appProvider = $scope.oauth.identityProviders[i];
if (appProvider.id != null && appProvider.id != false) {
selectedProviders[selectedProviders.length] = appProvider;
}
}
$scope.oauth.identityProviders = selectedProviders;
OAuthClient.update({
realm : realm.realm,
oauth : oauth.id
}, $scope.oauth, function() {
$scope.changed = false;
$route.reload();
Notifications.success("Your changes have been saved to the application.");
});
};
$scope.reset = function() {
$scope.oauth = angular.copy(oldCopy);
$scope.changed = false;
};
$scope.$watch('oauth', function() {
if (!angular.equals($scope.oauth, oldCopy)) {
$scope.changed = true;
}
}, true);
});
module.controller('OAuthClientProtocolMapperListCtrl', function($scope, realm, oauth, serverInfo,
OAuthClientProtocolMappersByProtocol,
$http, $location, Dialog, Notifications) {
$scope.realm = realm;
$scope.oauth = oauth;
if (oauth.protocol == null) {
oauth.protocol = 'openid-connect';
}
var protocolMappers = serverInfo.protocolMapperTypes[oauth.protocol];
var mapperTypes = {};
for (var i = 0; i < protocolMappers.length; i++) {
mapperTypes[protocolMappers[i].id] = protocolMappers[i];
}
$scope.mapperTypes = mapperTypes;
var updateMappers = function() {
$scope.mappers = OAuthClientProtocolMappersByProtocol.query({realm : realm.realm, oauth : oauth.id, protocol : oauth.protocol});
};
updateMappers();
});
module.controller('OAuthClientAddBuiltinProtocolMapperCtrl', function($scope, realm, oauth, serverInfo,
OAuthClientProtocolMappersByProtocol,
$http, $location, Dialog, Notifications) {
$scope.realm = realm;
$scope.oauth = oauth;
if (oauth.protocol == null) {
oauth.protocol = 'openid-connect';
}
var protocolMappers = serverInfo.protocolMapperTypes[oauth.protocol];
var mapperTypes = {};
for (var i = 0; i < protocolMappers.length; i++) {
mapperTypes[protocolMappers[i].id] = protocolMappers[i];
}
$scope.mapperTypes = mapperTypes;
var updateMappers = function() {
var appMappers = OAuthClientProtocolMappersByProtocol.query({realm : realm.realm, oauth : oauth.id, protocol : oauth.protocol}, function() {
var builtinMappers = serverInfo.builtinProtocolMappers[oauth.protocol];
for (var i = 0; i < appMappers.length; i++) {
for (var j = 0; j < builtinMappers.length; j++) {
if (builtinMappers[j].name == appMappers[i].name
&& builtinMappers[j].protocolMapper == appMappers[i].protocolMapper) {
console.log('removing: ' + builtinMappers[j].name);
builtinMappers.splice(j, 1);
break;
}
}
}
for (var j = 0; j < builtinMappers.length; j++) {
console.log('builtin left: ' + builtinMappers[j].name);
}
$scope.mappers = builtinMappers;
for (var i = 0; i < $scope.mappers.length; i++) {
$scope.mappers[i].isChecked = false;
}
});
};
updateMappers();
$scope.add = function() {
var toAdd = [];
for (var i = 0; i < $scope.mappers.length; i++) {
if ($scope.mappers[i].isChecked) {
delete $scope.mappers[i].isChecked;
toAdd.push($scope.mappers[i]);
}
}
$http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients-by-id/' + oauth.id + '/protocol-mappers/add-models',
toAdd).success(function() {
Notifications.success("Mappers added");
$location.url('/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/mappers');
}).error(function() {
Notifications.error("Error adding mappers");
$location.url('/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/mappers');
});
};
});
module.controller('OAuthClientProtocolMapperCtrl', function($scope, realm, serverInfo, oauth, mapper, OAuthClientProtocolMapper, Notifications, Dialog, $location) {
if (oauth.protocol == null) {
oauth.protocol = 'openid-connect';
}
$scope.realm = realm;
$scope.oauth = oauth;
$scope.create = false;
var protocol = oauth.protocol;
$scope.protocol = oauth.protocol;
$scope.mapper = angular.copy(mapper);
var oldCopy = angular.copy($scope.realm);
$scope.changed = false;
var protocolMappers = serverInfo.protocolMapperTypes[protocol];
for (var i = 0; i < protocolMappers.length; i++) {
if (protocolMappers[i].id == mapper.protocolMapper) {
$scope.mapperType = protocolMappers[i];
}
}
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
$scope.$watch('mapper', function() {
if (!angular.equals($scope.mapper, mapper)) {
$scope.changed = true;
}
}, true);
$scope.save = function() {
OAuthClientProtocolMapper.update({
realm : realm.realm,
oauth: oauth.id,
id : mapper.id
}, $scope.mapper, function() {
$scope.changed = false;
mapper = angular.copy($scope.mapper);
$location.url("/realms/" + realm.realm + '/oauth-clients/' + oauth.id + "/mappers/" + mapper.id);
Notifications.success("Your changes have been saved.");
});
};
$scope.reset = function() {
$scope.mapper = angular.copy(mapper);
$scope.changed = false;
};
$scope.cancel = function() {
//$location.url("/realms");
window.history.back();
};
$scope.remove = function() {
Dialog.confirmDelete($scope.mapper.name, 'mapper', function() {
OAuthClientProtocolMapper.remove({ realm: realm.realm, oauth: oauth.id, id : $scope.mapper.id }, function() {
Notifications.success("The mapper has been deleted.");
$location.url("/realms/" + realm.realm + '/oauth-clients/' + oauth.id + "/mappers");
});
});
};
});
module.controller('OAuthClientProtocolMapperCreateCtrl', function($scope, realm, serverInfo, oauth, OAuthClientProtocolMapper, Notifications, Dialog, $location) {
if (oauth.protocol == null) {
oauth.protocol = 'openid-connect';
}
$scope.realm = realm;
$scope.oauth = oauth;
$scope.create = true;
var protocol = oauth.protocol;
$scope.protocol = protocol;
$scope.mapper = { protocol : oauth.protocol, config: {}};
$scope.mapperTypes = serverInfo.protocolMapperTypes[protocol];
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
$scope.save = function() {
$scope.mapper.protocolMapper = $scope.mapperType.id;
OAuthClientProtocolMapper.save({
realm : realm.realm, oauth: oauth.id
}, $scope.mapper, function(data, headers) {
var l = headers().location;
var id = l.substring(l.lastIndexOf("/") + 1);
$location.url("/realms/" + realm.realm + '/oauth-clients/' + oauth.id + "/mappers/" + id);
Notifications.success("Mapper has been created.");
});
};
$scope.cancel = function() {
//$location.url("/realms");
window.history.back();
};
});

View file

@ -53,10 +53,6 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, WhoAmI, Current, $
return getAccess('view-realm') || this.manageRealm;
},
get viewApplications() {
return getAccess('view-applications') || this.manageApplications;
},
get viewClients() {
return getAccess('view-clients') || this.manageClients;
},
@ -73,10 +69,6 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, WhoAmI, Current, $
return getAccess('manage-realm');
},
get manageApplications() {
return getAccess('manage-applications');
},
get manageClients() {
return getAccess('manage-clients');
},
@ -484,7 +476,7 @@ module.controller('RealmRequiredCredentialsCtrl', function($scope, Realm, realm,
};
});
module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, applications, roles, Notifications, ApplicationRole, Application) {
module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, clients, roles, Notifications, ClientRole, Client) {
console.log('RealmDefaultRolesCtrl');
@ -494,17 +486,17 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, appli
$scope.selectedRealmRoles = [];
$scope.selectedRealmDefRoles = [];
$scope.applications = angular.copy(applications);
for (var i = 0; i < applications.length; i++) {
if (applications[i].name == 'account') {
$scope.application = $scope.applications[i];
$scope.clients = angular.copy(clients);
for (var i = 0; i < clients.length; i++) {
if (clients[i].name == 'account') {
$scope.client = $scope.clients[i];
break;
}
}
$scope.availableAppRoles = [];
$scope.selectedAppRoles = [];
$scope.selectedAppDefRoles = [];
$scope.availableClientRoles = [];
$scope.selectedClientRoles = [];
$scope.selectedClientDefRoles = [];
if (!$scope.realm.hasOwnProperty('defaultRoles') || $scope.realm.defaultRoles === null) {
$scope.realm.defaultRoles = [];
@ -558,81 +550,81 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, appli
});
};
$scope.changeApplication = function () {
$scope.changeClient = function () {
$scope.selectedAppRoles = [];
$scope.selectedAppDefRoles = [];
$scope.selectedClientRoles = [];
$scope.selectedClientDefRoles = [];
// Populate available roles for selected application
if ($scope.application) {
var appDefaultRoles = ApplicationRole.query({realm: $scope.realm.realm, application: $scope.application.id}, function () {
// Populate available roles for selected client
if ($scope.client) {
var appDefaultRoles = ClientRole.query({realm: $scope.realm.realm, client: $scope.client.id}, function () {
if (!$scope.application.hasOwnProperty('defaultRoles') || $scope.application.defaultRoles === null) {
$scope.application.defaultRoles = [];
if (!$scope.client.hasOwnProperty('defaultRoles') || $scope.client.defaultRoles === null) {
$scope.client.defaultRoles = [];
}
$scope.availableAppRoles = [];
$scope.availableClientRoles = [];
for (var i = 0; i < appDefaultRoles.length; i++) {
var roleName = appDefaultRoles[i].name;
if ($scope.application.defaultRoles.indexOf(roleName) < 0) {
$scope.availableAppRoles.push(roleName);
if ($scope.client.defaultRoles.indexOf(roleName) < 0) {
$scope.availableClientRoles.push(roleName);
}
}
});
} else {
$scope.availableAppRoles = null;
$scope.availableClientRoles = null;
}
};
$scope.addAppDefaultRole = function () {
$scope.addClientDefaultRole = function () {
// Remove selected roles from the app available roles and add them to app default roles (move from left to right).
for (var i = 0; i < $scope.selectedAppRoles.length; i++) {
var role = $scope.selectedAppRoles[i];
for (var i = 0; i < $scope.selectedClientRoles.length; i++) {
var role = $scope.selectedClientRoles[i];
var idx = $scope.application.defaultRoles.indexOf(role);
var idx = $scope.client.defaultRoles.indexOf(role);
if (idx < 0) {
$scope.application.defaultRoles.push(role);
$scope.client.defaultRoles.push(role);
}
idx = $scope.availableAppRoles.indexOf(role);
idx = $scope.availableClientRoles.indexOf(role);
if (idx != -1) {
$scope.availableAppRoles.splice(idx, 1);
$scope.availableClientRoles.splice(idx, 1);
}
}
// Update/save the selected application with new default roles.
Application.update({
// Update/save the selected client with new default roles.
Client.update({
realm: $scope.realm.realm,
application: $scope.application.id
}, $scope.application, function () {
Notifications.success("Your changes have been saved to the application.");
client: $scope.client.id
}, $scope.client, function () {
Notifications.success("Your changes have been saved to the client.");
});
};
$scope.rmAppDefaultRole = function () {
$scope.rmClientDefaultRole = function () {
// Remove selected roles from the app default roles and add them to app available roles (move from right to left).
for (var i = 0; i < $scope.selectedAppDefRoles.length; i++) {
var role = $scope.selectedAppDefRoles[i];
var idx = $scope.application.defaultRoles.indexOf(role);
for (var i = 0; i < $scope.selectedClientDefRoles.length; i++) {
var role = $scope.selectedClientDefRoles[i];
var idx = $scope.client.defaultRoles.indexOf(role);
if (idx != -1) {
$scope.application.defaultRoles.splice(idx, 1);
$scope.client.defaultRoles.splice(idx, 1);
}
idx = $scope.availableAppRoles.indexOf(role);
idx = $scope.availableClientRoles.indexOf(role);
if (idx < 0) {
$scope.availableAppRoles.push(role);
$scope.availableClientRoles.push(role);
}
}
// Update/save the selected application with new default roles.
Application.update({
// Update/save the selected client with new default roles.
Client.update({
realm: $scope.realm.realm,
application: $scope.application.id
}, $scope.application, function () {
Notifications.success("Your changes have been saved to the application.");
client: $scope.client.id
}, $scope.client, function () {
Notifications.success("Your changes have been saved to the client.");
});
};
@ -856,7 +848,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
alias : $scope.identityProvider.alias
}, function() {
$location.url("/realms/" + realm.realm + "/identity-provider-settings");
Notifications.success("The application has been deleted.");
Notifications.success("The client has been deleted.");
});
});
};
@ -1016,7 +1008,7 @@ module.controller('RealmKeysDetailCtrl', function($scope, Realm, realm, $http, $
};
});
module.controller('RealmSessionStatsCtrl', function($scope, realm, stats, RealmApplicationSessionStats, RealmLogoutAll, Notifications) {
module.controller('RealmSessionStatsCtrl', function($scope, realm, stats, RealmClientSessionStats, RealmLogoutAll, Notifications) {
$scope.realm = realm;
$scope.stats = stats;
@ -1081,7 +1073,7 @@ module.controller('RealmRevocationCtrl', function($scope, Realm, RealmPushRevoca
var msgStart = successCount>0 ? 'Successfully push notBefore to: ' + globalReqResult.successRequests + ' . ' : '';
Notifications.error(msgStart + 'Failed to push notBefore to: ' + globalReqResult.failedRequests + '. Verify availability of failed hosts and try again');
} else {
Notifications.success('Successfully push notBefore to all configured applications');
Notifications.success('Successfully push notBefore to all configured clients');
}
});
}
@ -1102,8 +1094,8 @@ module.controller('RoleListCtrl', function($scope, $location, realm, roles) {
});
module.controller('RoleDetailCtrl', function($scope, realm, role, roles, applications,
Role, ApplicationRole, RoleById, RoleRealmComposites, RoleApplicationComposites,
module.controller('RoleDetailCtrl', function($scope, realm, role, roles, clients,
Role, ClientRole, RoleById, RoleRealmComposites, RoleClientComposites,
$http, $location, Dialog, Notifications) {
$scope.realm = realm;
$scope.role = angular.copy(role);
@ -1149,8 +1141,8 @@ module.controller('RoleDetailCtrl', function($scope, realm, role, roles, applica
roleControl($scope, realm, role, roles, applications,
ApplicationRole, RoleById, RoleRealmComposites, RoleApplicationComposites,
roleControl($scope, realm, role, roles, clients,
ClientRole, RoleById, RoleRealmComposites, RoleClientComposites,
$http, $location, Notifications, Dialog);
});

View file

@ -1,17 +1,17 @@
module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, applications, Notifications, RealmRoleMapping,
ApplicationRoleMapping, AvailableRealmRoleMapping, AvailableApplicationRoleMapping,
CompositeRealmRoleMapping, CompositeApplicationRoleMapping) {
module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, clients, Notifications, RealmRoleMapping,
ClientRoleMapping, AvailableRealmRoleMapping, AvailableClientRoleMapping,
CompositeRealmRoleMapping, CompositeClientRoleMapping) {
$scope.realm = realm;
$scope.user = user;
$scope.selectedRealmRoles = [];
$scope.selectedRealmMappings = [];
$scope.realmMappings = [];
$scope.applications = applications;
$scope.applicationRoles = [];
$scope.applicationComposite = [];
$scope.selectedApplicationRoles = [];
$scope.selectedApplicationMappings = [];
$scope.applicationMappings = [];
$scope.clients = clients;
$scope.clientRoles = [];
$scope.clientComposite = [];
$scope.selectedClientRoles = [];
$scope.selectedClientMappings = [];
$scope.clientMappings = [];
$scope.dummymodel = [];
$scope.realmMappings = RealmRoleMapping.query({realm : realm.realm, userId : user.username});
@ -26,13 +26,13 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, ap
$scope.realmComposite = CompositeRealmRoleMapping.query({realm : realm.realm, userId : user.username});
$scope.selectedRealmMappings = [];
$scope.selectRealmRoles = [];
if ($scope.application) {
if ($scope.client) {
console.log('load available');
$scope.applicationComposite = CompositeApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.applicationRoles = AvailableApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.applicationMappings = ApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.selectedApplicationRoles = [];
$scope.selectedApplicationMappings = [];
$scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
$scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
$scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
$scope.selectedClientRoles = [];
$scope.selectedClientMappings = [];
}
Notifications.success("Role mappings updated.");
@ -47,57 +47,57 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, ap
$scope.realmComposite = CompositeRealmRoleMapping.query({realm : realm.realm, userId : user.username});
$scope.selectedRealmMappings = [];
$scope.selectRealmRoles = [];
if ($scope.application) {
if ($scope.client) {
console.log('load available');
$scope.applicationComposite = CompositeApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.applicationRoles = AvailableApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.applicationMappings = ApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.selectedApplicationRoles = [];
$scope.selectedApplicationMappings = [];
$scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
$scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
$scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
$scope.selectedClientRoles = [];
$scope.selectedClientMappings = [];
}
Notifications.success("Role mappings updated.");
});
};
$scope.addApplicationRole = function() {
$http.post(authUrl + '/admin/realms/' + realm.realm + '/users/' + user.username + '/role-mappings/applications-by-id/' + $scope.application.id,
$scope.selectedApplicationRoles).success(function() {
$scope.applicationMappings = ApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.applicationRoles = AvailableApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.applicationComposite = CompositeApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.selectedApplicationRoles = [];
$scope.selectedApplicationMappings = [];
$scope.addClientRole = function() {
$http.post(authUrl + '/admin/realms/' + realm.realm + '/users/' + user.username + '/role-mappings/clients-by-id/' + $scope.client.id,
$scope.selectedClientRoles).success(function() {
$scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
$scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
$scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
$scope.selectedClientRoles = [];
$scope.selectedClientMappings = [];
Notifications.success("Role mappings updated.");
});
};
$scope.deleteApplicationRole = function() {
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/users/' + user.username + '/role-mappings/applications-by-id/' + $scope.application.id,
{data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(function() {
$scope.applicationMappings = ApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.applicationRoles = AvailableApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.applicationComposite = CompositeApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.selectedApplicationRoles = [];
$scope.selectedApplicationMappings = [];
$scope.deleteClientRole = function() {
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/users/' + user.username + '/role-mappings/clients-by-id/' + $scope.client.id,
{data : $scope.selectedClientMappings, headers : {"content-type" : "application/json"}}).success(function() {
$scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
$scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
$scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
$scope.selectedClientRoles = [];
$scope.selectedClientMappings = [];
Notifications.success("Role mappings updated.");
});
};
$scope.changeApplication = function() {
console.log('changeApplication');
if ($scope.application) {
$scope.changeClient = function() {
console.log('changeClient');
if ($scope.client) {
console.log('load available');
$scope.applicationComposite = CompositeApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.applicationRoles = AvailableApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.applicationMappings = ApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.id});
$scope.clientComposite = CompositeClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
$scope.clientRoles = AvailableClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
$scope.clientMappings = ClientRoleMapping.query({realm : realm.realm, userId : user.username, client : $scope.client.id});
} else {
$scope.applicationRoles = null;
$scope.applicationMappings = null;
$scope.applicationComposite = null;
$scope.clientRoles = null;
$scope.clientMappings = null;
$scope.clientComposite = null;
}
$scope.selectedApplicationRoles = [];
$scope.selectedApplicationMappings = [];
$scope.selectedClientRoles = [];
$scope.selectedClientMappings = [];
};
@ -111,7 +111,7 @@ module.controller('UserSessionsCtrl', function($scope, realm, user, sessions, Us
$scope.logoutAll = function() {
UserLogout.save({realm : realm.realm, user: user.username}, function () {
Notifications.success('Logged out user in all applications');
Notifications.success('Logged out user in all clients');
UserSessions.query({realm: realm.realm, user: user.username}, function(updated) {
$scope.sessions = updated;
})

View file

@ -71,35 +71,24 @@ module.factory('RealmSessionStatsLoader', function(Loader, RealmSessionStats, $r
});
});
module.factory('RealmApplicationSessionStatsLoader', function(Loader, RealmApplicationSessionStats, $route, $q) {
return Loader.query(RealmApplicationSessionStats, function() {
module.factory('RealmClientSessionStatsLoader', function(Loader, RealmClientSessionStats, $route, $q) {
return Loader.query(RealmClientSessionStats, function() {
return {
realm : $route.current.params.realm
}
});
});
module.factory('ApplicationProtocolMapperLoader', function(Loader, ApplicationProtocolMapper, $route, $q) {
return Loader.get(ApplicationProtocolMapper, function() {
module.factory('ClientProtocolMapperLoader', function(Loader, ClientProtocolMapper, $route, $q) {
return Loader.get(ClientProtocolMapper, function() {
return {
realm : $route.current.params.realm,
application : $route.current.params.application,
client : $route.current.params.client,
id: $route.current.params.id
}
});
});
module.factory('OAuthClientProtocolMapperLoader', function(Loader, OAuthClientProtocolMapper, $route, $q) {
return Loader.get(OAuthClientProtocolMapper, function() {
return {
realm : $route.current.params.realm,
oauth : $route.current.params.oauth,
id: $route.current.params.id
}
});
});
module.factory('UserLoader', function(Loader, User, $route, $q) {
return Loader.get(User, function() {
return {
@ -175,74 +164,74 @@ module.factory('RoleListLoader', function(Loader, Role, $route, $q) {
});
});
module.factory('ApplicationRoleLoader', function(Loader, RoleById, $route, $q) {
module.factory('ClientRoleLoader', function(Loader, RoleById, $route, $q) {
return Loader.get(RoleById, function() {
return {
realm : $route.current.params.realm,
application : $route.current.params.application,
client : $route.current.params.client,
role : $route.current.params.role
}
});
});
module.factory('ApplicationSessionStatsLoader', function(Loader, ApplicationSessionStats, $route, $q) {
return Loader.get(ApplicationSessionStats, function() {
module.factory('ClientSessionStatsLoader', function(Loader, ClientSessionStats, $route, $q) {
return Loader.get(ClientSessionStats, function() {
return {
realm : $route.current.params.realm,
application : $route.current.params.application
client : $route.current.params.client
}
});
});
module.factory('ApplicationSessionCountLoader', function(Loader, ApplicationSessionCount, $route, $q) {
return Loader.get(ApplicationSessionCount, function() {
module.factory('ClientSessionCountLoader', function(Loader, ClientSessionCount, $route, $q) {
return Loader.get(ClientSessionCount, function() {
return {
realm : $route.current.params.realm,
application : $route.current.params.application
client : $route.current.params.client
}
});
});
module.factory('ApplicationClaimsLoader', function(Loader, ApplicationClaims, $route, $q) {
return Loader.get(ApplicationClaims, function() {
module.factory('ClientClaimsLoader', function(Loader, ClientClaims, $route, $q) {
return Loader.get(ClientClaims, function() {
return {
realm : $route.current.params.realm,
application : $route.current.params.application
client : $route.current.params.client
}
});
});
module.factory('ApplicationInstallationLoader', function(Loader, ApplicationInstallation, $route, $q) {
return Loader.get(ApplicationInstallation, function() {
module.factory('ClientInstallationLoader', function(Loader, ClientInstallation, $route, $q) {
return Loader.get(ClientInstallation, function() {
return {
realm : $route.current.params.realm,
application : $route.current.params.application
client : $route.current.params.client
}
});
});
module.factory('ApplicationRoleListLoader', function(Loader, ApplicationRole, $route, $q) {
return Loader.query(ApplicationRole, function() {
module.factory('ClientRoleListLoader', function(Loader, ClientRole, $route, $q) {
return Loader.query(ClientRole, function() {
return {
realm : $route.current.params.realm,
application : $route.current.params.application
client : $route.current.params.client
}
});
});
module.factory('ApplicationLoader', function(Loader, Application, $route, $q) {
return Loader.get(Application, function() {
module.factory('ClientLoader', function(Loader, Client, $route, $q) {
return Loader.get(Client, function() {
return {
realm : $route.current.params.realm,
application : $route.current.params.application
client : $route.current.params.client
}
});
});
module.factory('ApplicationListLoader', function(Loader, Application, $route, $q) {
return Loader.query(Application, function() {
module.factory('ClientListLoader', function(Loader, Client, $route, $q) {
return Loader.query(Client, function() {
return {
realm : $route.current.params.realm
}
@ -251,7 +240,7 @@ module.factory('ApplicationListLoader', function(Loader, Application, $route, $q
module.factory('RoleMappingLoader', function(Loader, RoleMapping, $route, $q) {
var realm = $route.current.params.realm || $route.current.params.application;
var realm = $route.current.params.realm || $route.current.params.client;
return Loader.query(RoleMapping, function() {
return {
@ -261,42 +250,6 @@ module.factory('RoleMappingLoader', function(Loader, RoleMapping, $route, $q) {
});
});
module.factory('OAuthClientLoader', function(Loader, OAuthClient, $route, $q) {
return Loader.get(OAuthClient, function() {
return {
realm : $route.current.params.realm,
oauth : $route.current.params.oauth
}
});
});
module.factory('OAuthClientClaimsLoader', function(Loader, OAuthClientClaims, $route, $q) {
return Loader.get(OAuthClientClaims, function() {
return {
realm : $route.current.params.realm,
oauth : $route.current.params.oauth
}
});
});
module.factory('OAuthClientListLoader', function(Loader, OAuthClient, $route, $q) {
return Loader.query(OAuthClient, function() {
return {
realm : $route.current.params.realm
}
});
});
module.factory('OAuthClientInstallationLoader', function(Loader, OAuthClientInstallation, $route, $q) {
return Loader.get(OAuthClientInstallation, function() {
return {
realm : $route.current.params.realm,
oauth : $route.current.params.oauth
}
});
});
module.factory('IdentityProviderLoader', function(Loader, IdentityProvider, $route, $q) {
return Loader.get(IdentityProvider, function () {
return {

View file

@ -190,10 +190,10 @@ module.factory('ServerInfo', function($resource) {
module.factory('ApplicationProtocolMapper', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/protocol-mappers/models/:id', {
module.factory('ClientProtocolMapper', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/protocol-mappers/models/:id', {
realm : '@realm',
application: '@application',
client: '@client',
id : "@id"
}, {
update : {
@ -202,29 +202,6 @@ module.factory('ApplicationProtocolMapper', function($resource) {
});
});
module.factory('OAuthClientProtocolMapper', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/protocol-mappers/models/:id', {
realm : '@realm',
oauth: '@oauth',
id : "@id"
}, {
update : {
method : 'PUT'
}
});
});
module.factory('OAuthClientProtocolMappersByProtocol', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/protocol-mappers/protocol/:protocol', {
realm : '@realm',
oauth : "@oauth",
protocol : "@protocol"
});
});
module.factory('User', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users/:userId', {
realm : '@realm',
@ -347,72 +324,72 @@ module.factory('AvailableRealmRoleMapping', function($resource) {
});
module.factory('ApplicationRoleMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users/:userId/role-mappings/applications-by-id/:application', {
module.factory('ClientRoleMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users/:userId/role-mappings/clients-by-id/:client', {
realm : '@realm',
userId : '@userId',
application : "@application"
client : "@client"
});
});
module.factory('AvailableApplicationRoleMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users/:userId/role-mappings/applications-by-id/:application/available', {
module.factory('AvailableClientRoleMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users/:userId/role-mappings/clients-by-id/:client/available', {
realm : '@realm',
userId : '@userId',
application : "@application"
client : "@client"
});
});
module.factory('CompositeApplicationRoleMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users/:userId/role-mappings/applications-by-id/:application/composite', {
module.factory('CompositeClientRoleMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users/:userId/role-mappings/clients-by-id/:client/composite', {
realm : '@realm',
userId : '@userId',
application : "@application"
client : "@client"
});
});
module.factory('ApplicationRealmScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/scope-mappings/realm', {
module.factory('ClientRealmScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/scope-mappings/realm', {
realm : '@realm',
application : '@application'
client : '@client'
});
});
module.factory('ApplicationAvailableRealmScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/scope-mappings/realm/available', {
module.factory('ClientAvailableRealmScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/scope-mappings/realm/available', {
realm : '@realm',
application : '@application'
client : '@client'
});
});
module.factory('ApplicationCompositeRealmScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/scope-mappings/realm/composite', {
module.factory('ClientCompositeRealmScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/scope-mappings/realm/composite', {
realm : '@realm',
application : '@application'
client : '@client'
});
});
module.factory('ApplicationApplicationScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/scope-mappings/applications-by-id/:targetApp', {
module.factory('ClientClientScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/scope-mappings/clients-by-id/:targetClient', {
realm : '@realm',
application : '@application',
targetApp : '@targetApp'
client : '@client',
targetClient : '@targetClient'
});
});
module.factory('ApplicationAvailableApplicationScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/scope-mappings/applications-by-id/:targetApp/available', {
module.factory('ClientAvailableClientScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/scope-mappings/clients-by-id/:targetClient/available', {
realm : '@realm',
application : '@application',
targetApp : '@targetApp'
client : '@client',
targetClient : '@targetClient'
});
});
module.factory('ApplicationCompositeApplicationScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/scope-mappings/applications-by-id/:targetApp/composite', {
module.factory('ClientCompositeClientScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/scope-mappings/clients-by-id/:targetClient/composite', {
realm : '@realm',
application : '@application',
targetApp : '@targetApp'
client : '@client',
targetClient : '@targetClient'
});
});
@ -443,24 +420,24 @@ module.factory('RealmSessionStats', function($resource) {
});
});
module.factory('RealmApplicationSessionStats', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/application-by-id-session-stats', {
module.factory('RealmClientSessionStats', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/client-by-id-session-stats', {
realm : '@realm'
});
});
module.factory('RoleApplicationComposites', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/roles-by-id/:role/composites/applications-by-id/:application', {
module.factory('RoleClientComposites', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/roles-by-id/:role/composites/clients-by-id/:client', {
realm : '@realm',
role : '@role',
application : "@application"
client : "@client"
});
});
function roleControl($scope, realm, role, roles, applications,
ApplicationRole, RoleById, RoleRealmComposites, RoleApplicationComposites,
function roleControl($scope, realm, role, roles, clients,
ClientRole, RoleById, RoleRealmComposites, RoleClientComposites,
$http, $location, Notifications, Dialog) {
$scope.$watch(function () {
@ -499,11 +476,11 @@ function roleControl($scope, realm, role, roles, applications,
$scope.selectedRealmRoles = [];
$scope.selectedRealmMappings = [];
$scope.realmMappings = [];
$scope.applications = applications;
$scope.applicationRoles = [];
$scope.selectedApplicationRoles = [];
$scope.selectedApplicationMappings = [];
$scope.applicationMappings = [];
$scope.clients = clients;
$scope.clientRoles = [];
$scope.selectedClientRoles = [];
$scope.selectedClientMappings = [];
$scope.clientMappings = [];
for (var j = 0; j < $scope.realmRoles.length; j++) {
if ($scope.realmRoles[j].id == role.id) {
@ -563,61 +540,61 @@ function roleControl($scope, realm, role, roles, applications,
});
};
$scope.addApplicationRole = function() {
$scope.addClientRole = function() {
$scope.compositeSwitchDisabled=true;
$http.post(authUrl + '/admin/realms/' + realm.realm + '/roles-by-id/' + role.id + '/composites',
$scope.selectedApplicationRoles).success(function() {
for (var i = 0; i < $scope.selectedApplicationRoles.length; i++) {
var role = $scope.selectedApplicationRoles[i];
var idx = $scope.applicationRoles.indexOf($scope.selectedApplicationRoles[i]);
$scope.selectedClientRoles).success(function() {
for (var i = 0; i < $scope.selectedClientRoles.length; i++) {
var role = $scope.selectedClientRoles[i];
var idx = $scope.clientRoles.indexOf($scope.selectedClientRoles[i]);
if (idx != -1) {
$scope.applicationRoles.splice(idx, 1);
$scope.applicationMappings.push(role);
$scope.clientRoles.splice(idx, 1);
$scope.clientMappings.push(role);
}
}
$scope.selectedApplicationRoles = [];
$scope.selectedClientRoles = [];
});
};
$scope.deleteApplicationRole = function() {
$scope.deleteClientRole = function() {
$scope.compositeSwitchDisabled=true;
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/roles-by-id/' + role.id + '/composites',
{data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(function() {
for (var i = 0; i < $scope.selectedApplicationMappings.length; i++) {
var role = $scope.selectedApplicationMappings[i];
var idx = $scope.applicationMappings.indexOf($scope.selectedApplicationMappings[i]);
{data : $scope.selectedClientMappings, headers : {"content-type" : "application/json"}}).success(function() {
for (var i = 0; i < $scope.selectedClientMappings.length; i++) {
var role = $scope.selectedClientMappings[i];
var idx = $scope.clientMappings.indexOf($scope.selectedClientMappings[i]);
if (idx != -1) {
$scope.applicationMappings.splice(idx, 1);
$scope.applicationRoles.push(role);
$scope.clientMappings.splice(idx, 1);
$scope.clientRoles.push(role);
}
}
$scope.selectedApplicationMappings = [];
$scope.selectedClientMappings = [];
});
};
$scope.changeApplication = function() {
$scope.applicationRoles = ApplicationRole.query({realm : realm.realm, application : $scope.compositeApp.id}, function() {
$scope.applicationMappings = RoleApplicationComposites.query({realm : realm.realm, role : role.id, application : $scope.compositeApp.id}, function(){
for (var i = 0; i < $scope.applicationMappings.length; i++) {
var role = $scope.applicationMappings[i];
for (var j = 0; j < $scope.applicationRoles.length; j++) {
var realmRole = $scope.applicationRoles[j];
$scope.changeClient = function() {
$scope.clientRoles = ClientRole.query({realm : realm.realm, client : $scope.compositeClient.id}, function() {
$scope.clientMappings = RoleClientComposites.query({realm : realm.realm, role : role.id, client : $scope.compositeClient.id}, function(){
for (var i = 0; i < $scope.clientMappings.length; i++) {
var role = $scope.clientMappings[i];
for (var j = 0; j < $scope.clientRoles.length; j++) {
var realmRole = $scope.clientRoles[j];
if (realmRole.id == role.id) {
var idx = $scope.applicationRoles.indexOf(realmRole);
var idx = $scope.clientRoles.indexOf(realmRole);
if (idx != -1) {
$scope.applicationRoles.splice(idx, 1);
$scope.clientRoles.splice(idx, 1);
break;
}
}
}
}
});
for (var j = 0; j < $scope.applicationRoles.length; j++) {
if ($scope.applicationRoles[j] == role.id) {
var appRole = $scope.applicationRoles[j];
var idx = $scope.applicationRoles.indexof(appRole);
$scope.applicationRoles.splice(idx, 1);
for (var j = 0; j < $scope.clientRoles.length; j++) {
if ($scope.clientRoles[j] == role.id) {
var appRole = $scope.clientRoles[j];
var idx = $scope.clientRoles.indexof(appRole);
$scope.clientRoles.splice(idx, 1);
break;
}
}
@ -653,10 +630,10 @@ module.factory('RoleById', function($resource) {
});
});
module.factory('ApplicationRole', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/roles/:role', {
module.factory('ClientRole', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/roles/:role', {
realm : '@realm',
application : "@application",
client : "@client",
role : '@role'
}, {
update : {
@ -665,10 +642,10 @@ module.factory('ApplicationRole', function($resource) {
});
});
module.factory('ApplicationClaims', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/claims', {
module.factory('ClientClaims', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/claims', {
realm : '@realm',
application : "@application"
client : "@client"
}, {
update : {
method : 'PUT'
@ -676,52 +653,52 @@ module.factory('ApplicationClaims', function($resource) {
});
});
module.factory('ApplicationProtocolMappersByProtocol', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/protocol-mappers/protocol/:protocol', {
module.factory('ClientProtocolMappersByProtocol', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/protocol-mappers/protocol/:protocol', {
realm : '@realm',
application : "@application",
client : "@client",
protocol : "@protocol"
});
});
module.factory('ApplicationSessionStats', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/session-stats', {
module.factory('ClientSessionStats', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/session-stats', {
realm : '@realm',
application : "@application"
client : "@client"
});
});
module.factory('ApplicationSessionStatsWithUsers', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/session-stats?users=true', {
module.factory('ClientSessionStatsWithUsers', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/session-stats?users=true', {
realm : '@realm',
application : "@application"
client : "@client"
});
});
module.factory('ApplicationSessionCount', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/session-count', {
module.factory('ClientSessionCount', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/session-count', {
realm : '@realm',
application : "@application"
client : "@client"
});
});
module.factory('ApplicationUserSessions', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/user-sessions', {
module.factory('ClientUserSessions', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/user-sessions', {
realm : '@realm',
application : "@application"
client : "@client"
});
});
module.factory('ApplicationLogoutAll', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/logout-all', {
module.factory('ClientLogoutAll', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/logout-all', {
realm : '@realm',
application : "@application"
client : "@client"
});
});
module.factory('ApplicationLogoutUser', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/logout-user/:user', {
module.factory('ClientLogoutUser', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/logout-user/:user', {
realm : '@realm',
application : "@application",
client : "@client",
user : "@user"
});
});
@ -731,39 +708,39 @@ module.factory('RealmLogoutAll', function($resource) {
});
});
module.factory('ApplicationPushRevocation', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/push-revocation', {
module.factory('ClientPushRevocation', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/push-revocation', {
realm : '@realm',
application : "@application"
client : "@client"
});
});
module.factory('ApplicationClusterNode', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/nodes/:node', {
module.factory('ClientClusterNode', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/nodes/:node', {
realm : '@realm',
application : "@application"
client : "@client"
});
});
module.factory('ApplicationTestNodesAvailable', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/test-nodes-available', {
module.factory('ClientTestNodesAvailable', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/test-nodes-available', {
realm : '@realm',
application : "@application"
client : "@client"
});
});
module.factory('ApplicationCertificate', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/:attribute', {
module.factory('ClientCertificate', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/certificates/:attribute', {
realm : '@realm',
application : "@application",
client : "@client",
attribute: "@attribute"
});
});
module.factory('ApplicationCertificateGenerate', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/:attribute/generate', {
module.factory('ClientCertificateGenerate', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/certificates/:attribute/generate', {
realm : '@realm',
application : "@application",
client : "@client",
attribute: "@attribute"
},
{
@ -773,10 +750,10 @@ module.factory('ApplicationCertificateGenerate', function($resource) {
});
});
module.factory('ApplicationCertificateDownload', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/:attribute/download', {
module.factory('ClientCertificateDownload', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/certificates/:attribute/download', {
realm : '@realm',
application : "@application",
client : "@client",
attribute: "@attribute"
},
{
@ -787,10 +764,10 @@ module.factory('ApplicationCertificateDownload', function($resource) {
});
});
module.factory('Application', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application', {
module.factory('Client', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client', {
realm : '@realm',
application : '@application'
client : '@client'
}, {
update : {
method : 'PUT'
@ -798,29 +775,29 @@ module.factory('Application', function($resource) {
});
});
module.factory('ApplicationInstallation', function($resource) {
var url = authUrl + '/admin/realms/:realm/applications-by-id/:application/installation/json';
module.factory('ClientInstallation', function($resource) {
var url = authUrl + '/admin/realms/:realm/clients-by-id/:client/installation/json';
return {
url : function(parameters)
{
return url.replace(':realm', parameters.realm).replace(':application', parameters.application);
return url.replace(':realm', parameters.realm).replace(':client', parameters.client);
}
}
});
module.factory('ApplicationInstallationJBoss', function($resource) {
var url = authUrl + '/admin/realms/:realm/applications-by-id/:application/installation/jboss';
module.factory('ClientInstallationJBoss', function($resource) {
var url = authUrl + '/admin/realms/:realm/clients-by-id/:client/installation/jboss';
return {
url : function(parameters)
{
return url.replace(':realm', parameters.realm).replace(':application', parameters.application);
return url.replace(':realm', parameters.realm).replace(':client', parameters.client);
}
}
});
module.factory('ApplicationCredentials', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/client-secret', {
module.factory('ClientCredentials', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/client-secret', {
realm : '@realm',
application : '@application'
client : '@client'
}, {
update : {
method : 'POST'
@ -828,10 +805,10 @@ module.factory('ApplicationCredentials', function($resource) {
});
});
module.factory('ApplicationOrigins', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/allowed-origins', {
module.factory('ClientOrigins', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients-by-id/:client/allowed-origins', {
realm : '@realm',
application : '@application'
client : '@client'
}, {
update : {
method : 'PUT',
@ -840,127 +817,13 @@ module.factory('ApplicationOrigins', function($resource) {
});
});
module.factory('OAuthClient', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth', {
realm : '@realm',
oauth : '@oauth'
}, {
update : {
method : 'PUT'
}
});
});
module.factory('OAuthClientClaims', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/claims', {
realm : '@realm',
oauth : "@oauth"
}, {
update : {
method : 'PUT'
}
});
});
module.factory('OAuthClientCredentials', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/client-secret', {
realm : '@realm',
oauth : '@oauth'
}, {
update : {
method : 'POST'
}
});
});
module.factory('OAuthCertificate', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/certificates', {
realm : '@realm',
oauth : '@oauth'
});
});
module.factory('OAuthCertificateDownload', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/certificates/download', {
realm : '@realm',
oauth : '@oauth'
});
});
module.factory('OAuthClientRealmScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/scope-mappings/realm', {
realm : '@realm',
oauth : '@oauth'
});
});
module.factory('OAuthClientCompositeRealmScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/scope-mappings/realm/composite', {
realm : '@realm',
oauth : '@oauth'
});
});
module.factory('OAuthClientAvailableRealmScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/scope-mappings/realm/available', {
realm : '@realm',
oauth : '@oauth'
});
});
module.factory('OAuthClientApplicationScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/scope-mappings/applications-by-id/:targetApp', {
realm : '@realm',
oauth : '@oauth',
targetApp : '@targetApp'
});
});
module.factory('OAuthClientCompositeApplicationScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/scope-mappings/applications-by-id/:targetApp/composite', {
realm : '@realm',
oauth : '@oauth',
targetApp : '@targetApp'
});
});
module.factory('OAuthClientAvailableApplicationScopeMapping', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/scope-mappings/applications-by-id/:targetApp/available', {
realm : '@realm',
oauth : '@oauth',
targetApp : '@targetApp'
});
});
module.factory('OAuthClientInstallation', function($resource) {
var url = authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/installation';
var resource = $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/installation', {
realm : '@realm',
oauth : '@oauth'
}, {
update : {
method : 'PUT'
}
});
resource.url = function(parameters) {
return url.replace(':realm', parameters.realm).replace(':oauth', parameters.oauth);
}
return resource;
});
module.factory('Current', function(Realm, $route) {
var current = {};
current.realms = {};
current.realm = null;
current.applications = {};
current.application = null;
current.clients = {};
current.client = null;
current.refresh = function() {
current.realm = null;

View file

@ -1,59 +0,0 @@
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="resourceUrl + '/partials/realm-menu.html'"></div>
<div id="content-area" class="col-md-9" role="main">
<h2></h2>
<div id="content">
<h2><span>{{realm.realm}}</span> Applications <span tooltip-placement="right" tooltip="Applications are trusted browser apps and web services in a realm. These applications can request a login. You can also define application specific roles." class="fa fa-info-circle"></span></h2>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th class="kc-table-actions" colspan="3">
<div class="search-comp clearfix">
<input type="text" placeholder="Search..." class="form-control search" data-ng-model="search.name"
onkeyup="if(event.keyCode == 13){$(this).next('button').click();}">
<button type="submit" class="kc-icon-search" tooltip-placement="right"
tooltip="Search by application name.">
Icon: search
</button>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="#/import/application/{{realm.realm}}" data-ng-show="importButton">Import</a>
<a class="btn btn-primary" href="#/create/application/{{realm.realm}}">Create</a>
</div>
</th>
</tr>
<tr data-ng-hide="applications.length == 0">
<th>Application Name</th>
<th>Enabled</th>
<th>Base URL</th>
</tr>
</thead>
<!--<tfoot data-ng-show="applications && applications.length > 5">
<tr>
<td colspan="3">
<div class="table-nav">
<a href="#" class="first disabled">First page</a><a href="#" class="prev disabled">Previous
page</a><span><strong>1-8</strong> of <strong>10</strong></span><a href="#"
class="next">Next
page</a><a href="#" class="last">Last page</a>
</div>
</td>
</tr>
</tfoot>-->
<tbody>
<tr ng-repeat="app in applications | filter:search">
<td><a href="#/realms/{{realm.realm}}/applications/{{app.id}}">{{app.name}}</a></td>
<td>{{app.enabled}}</td>
<td ng-class="{'text-muted': !app.baseUrl}">
<a href="{{app.baseUrl}}" data-ng-show="app.baseUrl">{{app.baseUrl}}</a>
<span data-ng-hide="app.baseUrl">Not defined</span>
</tr>
<tr data-ng-show="applications.length == 0">
<td>No applications available</td>
</tr>
</tbody>
</table>
<div class="feedback warning inline" data-ng-show="search && applications.length == 0">
<p><strong>Your search returned no results.</strong><br>Try modifying the query and try again.</p>
</div>
</div>
</div>

View file

@ -1,47 +0,0 @@
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="resourceUrl + '/partials/realm-menu.html'"></div>
<div id="content-area" class="col-md-9" role="main">
<kc-navigation-application></kc-navigation-application>
<div id="content">
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">{{application.name}}</a></li>
<li class="active">Protocol Mappers</li>
</ol>
<h2><span>{{realm.realm}} </span> {{application.name}} {{application.protocol}} Protocol Mappers <span tooltip-placement="right" tooltip="Protocol mappers perform transformation on tokens and documents. They an do things like map user data into protocol claims, or just transform any requests going between the application and auth server." class="fa fa-info-circle"></span></h2>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th class="kc-table-actions" colspan="4">
<div class="search-comp clearfix">
<input type="text" placeholder="Search..." class="form-control search" data-ng-model="search.name"
onkeyup="if(event.keyCode == 13){$(this).next('button').click();}">
<button type="submit" class="kc-icon-search" tooltip-placement="right"
tooltip="Search by mapper name.">
Icon: search
</button>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="#/create/application/{{realm.realm}}/{{application.id}}/mappers">Create</a>
<a class="btn btn-primary" href="#/realms/{{realm.realm}}/applications/{{application.id}}/add-mappers">Add Builtin</a>
</div>
</th>
</tr>
<tr data-ng-hide="mappers.length == 0">
<th>Name</th>
<th>Category</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="mapper in mappers | filter:search">
<td><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/mappers/{{mapper.id}}">{{mapper.name}}</a></td>
<td>{{mapperTypes[mapper.protocolMapper].category}}</td>
<td>{{mapperTypes[mapper.protocolMapper].name}}</td>
</tr>
<tr data-ng-show="mappers.length == 0">
<td>No mappers available</td>
</tr>
</tbody>
</table>
</div>
</div>

View file

@ -1,16 +1,16 @@
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="resourceUrl + '/partials/realm-menu.html'"></div>
<div id="content-area" class="col-md-9" role="main">
<kc-navigation-application></kc-navigation-application>
<kc-navigation-client></kc-navigation-client>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">{{application.name}}</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/clustering">Clustering</a></li>
<li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering">Clustering</a></li>
<li class="active">{{node.host}}</li>
</ol>
<h2 data-ng-show="create || registered"><span>{{application.name}} Clustering</span></h2>
<h2 data-ng-show="create || registered"><span>{{client.clientId}} Clustering</span></h2>
<h2 data-ng-hide="create || registered">Cluster node on host <span>{{node.host}}</span> not registered!</h2>
<form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!access.manageApplications" data-ng-show="create || registered">
<form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!access.manageClients" data-ng-show="create || registered">
<fieldset >
<legend><span class="text">Configuration of cluster node</span></legend>
<div class="form-group">

View file

@ -1,14 +1,14 @@
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="resourceUrl + '/partials/realm-menu.html'"></div>
<div id="content-area" class="col-md-9" role="main">
<kc-navigation-application></kc-navigation-application>
<kc-navigation-client></kc-navigation-client>
<div id="content">
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
<li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">{{application.name}}</a></li>
<li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
<li class="active">Clustering</li>
</ol>
<h2 data-ng-hide="create"><span>{{application.name}}</span> Clustering</h2>
<form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!access.manageApplications">
<h2 data-ng-hide="create"><span>{{client.clientId}}</span> Clustering</h2>
<form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!access.manageClients">
<legend><span class="text">Basic configuration</span></legend>
<fieldset >
<div class="form-group clearfix">
@ -17,12 +17,12 @@
<div class="row">
<div class="col-sm-4">
<input class="form-control" type="number" required
max="31536000" data-ng-model="application.nodeReRegistrationTimeout"
max="31536000" data-ng-model="client.nodeReRegistrationTimeout"
id="nodeReRegistrationTimeout" name="nodeReRegistrationTimeout"/>
</div>
<div class="col-sm-4 select-kc">
<select name="nodeReRegistrationTimeoutUnit" data-ng-model="application.nodeReRegistrationTimeoutUnit" >
<option data-ng-selected="!application.nodeReRegistrationTimeoutUnit">Seconds</option>
<select name="nodeReRegistrationTimeoutUnit" data-ng-model="client.nodeReRegistrationTimeoutUnit" >
<option data-ng-selected="!client.nodeReRegistrationTimeoutUnit">Seconds</option>
<option>Minutes</option>
<option>Hours</option>
<option>Days</option>
@ -30,7 +30,7 @@
</div>
</div>
</div>
<span tooltip-placement="right" tooltip="Interval to specify max time for registered application cluster nodes to re-register. If cluster node won't send re-registration request to Keycloak within this time, it will be unregistered from Keycloak" class="fa fa-info-circle"></span>
<span tooltip-placement="right" tooltip="Interval to specify max time for registered clients cluster nodes to re-register. If cluster node won't send re-registration request to Keycloak within this time, it will be unregistered from Keycloak" class="fa fa-info-circle"></span>
</div>
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
<button data-kc-reset data-ng-show="changed">Clear changes</button>
@ -43,10 +43,10 @@
<table class="table table-striped table-bordered">
<thead>
<tr>
<th class="kc-table-actions" colspan="3" data-ng-show="access.manageApplications">
<th class="kc-table-actions" colspan="3" data-ng-show="access.manageClients">
<div class="pull-right">
<a class="btn btn-primary" tooltip="Manually register cluster node. This is usually not needed as cluster node should be registered automatically by adapter"
tooltip-placement="bottom" href="#/register-node/realms/{{realm.realm}}/applications/{{application.id}}/clustering">Register node manually</a>
tooltip-placement="bottom" href="#/register-node/realms/{{realm.realm}}/clients/{{client.id}}/clustering">Register node manually</a>
</div>
</th>
</tr>
@ -57,7 +57,7 @@
</thead>
<tbody>
<tr ng-repeat="node in nodeRegistrations">
<td><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/clustering/{{node.host}}">{{node.host}}</a></td>
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering/{{node.host}}">{{node.host}}</a></td>
<td>{{node.lastRegistration}}</td>
</tr>
<tr data-ng-show="!nodeRegistrations || nodeRegistrations.length == 0">

Some files were not shown because too many files have changed in this diff Show more