keycloak-aplcache
Changes
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java 6(+6 -0)
server-spi-private/src/main/java/org/keycloak/models/utils/AuthenticationFlowResolver.java 57(+57 -0)
testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/forms/UsernameOnlyAuthenticator.java 137(+137 -0)
testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory 3(+2 -1)
Details
diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
index a7ca83e..77c2c45 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
@@ -56,6 +56,7 @@ public class ClientRepresentation {
protected Boolean frontchannelLogout;
protected String protocol;
protected Map<String, String> attributes;
+ protected Map<String, String> authenticationFlowBindingOverrides;
protected Boolean fullScopeAllowed;
protected Integer nodeReRegistrationTimeout;
protected Map<String, Integer> registeredNodes;
@@ -296,6 +297,14 @@ public class ClientRepresentation {
this.attributes = attributes;
}
+ public Map<String, String> getAuthenticationFlowBindingOverrides() {
+ return authenticationFlowBindingOverrides;
+ }
+
+ public void setAuthenticationFlowBindingOverrides(Map<String, String> authenticationFlowBindingOverrides) {
+ this.authenticationFlowBindingOverrides = authenticationFlowBindingOverrides;
+ }
+
public Integer getNodeReRegistrationTimeout() {
return nodeReRegistrationTimeout;
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
index 14f4c0f..a5823ce 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
@@ -346,6 +346,34 @@ public class ClientAdapter implements ClientModel {
}
@Override
+ public void setAuthenticationFlowBindingOverride(String name, String value) {
+ getDelegateForUpdate();
+ updated.setAuthenticationFlowBindingOverride(name, value);
+
+ }
+
+ @Override
+ public void removeAuthenticationFlowBindingOverride(String name) {
+ getDelegateForUpdate();
+ updated.removeAuthenticationFlowBindingOverride(name);
+
+ }
+
+ @Override
+ public String getAuthenticationFlowBindingOverride(String name) {
+ if (isUpdated()) return updated.getAuthenticationFlowBindingOverride(name);
+ return cached.getAuthFlowBindings().get(name);
+ }
+
+ @Override
+ public Map<String, String> getAuthenticationFlowBindingOverrides() {
+ if (isUpdated()) return updated.getAuthenticationFlowBindingOverrides();
+ Map<String, String> copy = new HashMap<String, String>();
+ copy.putAll(cached.getAuthFlowBindings());
+ return copy;
+ }
+
+ @Override
public Set<ProtocolMapperModel> getProtocolMappers() {
if (isUpdated()) return updated.getProtocolMappers();
return cached.getProtocolMappers();
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java
index b4b6729..787dc45 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java
@@ -46,6 +46,7 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
protected String registrationToken;
protected String protocol;
protected Map<String, String> attributes = new HashMap<String, String>();
+ protected Map<String, String> authFlowBindings = new HashMap<String, String>();
protected boolean publicClient;
protected boolean fullScopeAllowed;
protected boolean frontchannelLogout;
@@ -83,6 +84,7 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
enabled = model.isEnabled();
protocol = model.getProtocol();
attributes.putAll(model.getAttributes());
+ authFlowBindings.putAll(model.getAuthenticationFlowBindingOverrides());
notBefore = model.getNotBefore();
frontchannelLogout = model.isFrontchannelLogout();
publicClient = model.isPublicClient();
@@ -256,4 +258,8 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
public boolean isUseTemplateMappers() {
return useTemplateMappers;
}
+
+ public Map<String, String> getAuthFlowBindings() {
+ return authFlowBindings;
+ }
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
index ac1a738..3a7eabb 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
@@ -271,6 +271,29 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
}
@Override
+ public void setAuthenticationFlowBindingOverride(String name, String value) {
+ entity.getAuthFlowBindings().put(name, value);
+
+ }
+
+ @Override
+ public void removeAuthenticationFlowBindingOverride(String name) {
+ entity.getAuthFlowBindings().remove(name);
+ }
+
+ @Override
+ public String getAuthenticationFlowBindingOverride(String name) {
+ return entity.getAuthFlowBindings().get(name);
+ }
+
+ @Override
+ public Map<String, String> getAuthenticationFlowBindingOverrides() {
+ Map<String, String> copy = new HashMap<>();
+ copy.putAll(entity.getAuthFlowBindings());
+ return copy;
+ }
+
+ @Override
public void setAttribute(String name, String value) {
entity.getAttributes().put(name, value);
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
index dab3fe4..7f88977 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
@@ -119,6 +119,12 @@ public class ClientEntity {
@CollectionTable(name="CLIENT_ATTRIBUTES", joinColumns={ @JoinColumn(name="CLIENT_ID") })
protected Map<String, String> attributes = new HashMap<String, String>();
+ @ElementCollection
+ @MapKeyColumn(name="BINDING_NAME")
+ @Column(name="FLOW_ID", length = 4000)
+ @CollectionTable(name="CLIENT_AUTH_FLOW_BINDINGS", joinColumns={ @JoinColumn(name="CLIENT_ID") })
+ protected Map<String, String> authFlowBindings = new HashMap<String, String>();
+
@OneToMany(fetch = FetchType.LAZY, mappedBy = "client", cascade = CascadeType.REMOVE)
Collection<ClientIdentityProviderMappingEntity> identityProviders = new ArrayList<ClientIdentityProviderMappingEntity>();
@@ -292,6 +298,14 @@ public class ClientEntity {
this.attributes = attributes;
}
+ public Map<String, String> getAuthFlowBindings() {
+ return authFlowBindings;
+ }
+
+ public void setAuthFlowBindings(Map<String, String> authFlowBindings) {
+ this.authFlowBindings = authFlowBindings;
+ }
+
public String getProtocol() {
return protocol;
}
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-4.0.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-4.0.0.xml
new file mode 100644
index 0000000..f7ebc68
--- /dev/null
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-4.0.0.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+ ~ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ ~ * and other contributors as indicated by the @author tags.
+ ~ *
+ ~ * Licensed under the Apache License, Version 2.0 (the "License");
+ ~ * you may not use this file except in compliance with the License.
+ ~ * You may obtain a copy of the License at
+ ~ *
+ ~ * http://www.apache.org/licenses/LICENSE-2.0
+ ~ *
+ ~ * Unless required by applicable law or agreed to in writing, software
+ ~ * distributed under the License is distributed on an "AS IS" BASIS,
+ ~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ * See the License for the specific language governing permissions and
+ ~ * limitations under the License.
+ -->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
+
+ <changeSet author="bburke@redhat.com" id="4.0.0-KEYCLOAK-6335">
+ <createTable tableName="CLIENT_AUTH_FLOW_BINDINGS">
+ <column name="CLIENT_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="FLOW_ID" type="VARCHAR(36)"/>
+ <column name="BINDING_NAME" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <addPrimaryKey columnNames="CLIENT_ID, BINDING_NAME" constraintName="C_CLI_FLOW_BIND" tableName="CLIENT_AUTH_FLOW_BINDINGS"/>
+ </changeSet>
+</databaseChangeLog>
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
index d89aa96..fa824e2 100755
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -53,4 +53,5 @@
<include file="META-INF/jpa-changelog-3.4.0.xml"/>
<include file="META-INF/jpa-changelog-3.4.1.xml"/>
<include file="META-INF/jpa-changelog-3.4.2.xml"/>
+ <include file="META-INF/jpa-changelog-4.0.0.xml"/>
</databaseChangeLog>
diff --git a/server-spi/src/main/java/org/keycloak/models/AuthenticationFlowBindings.java b/server-spi/src/main/java/org/keycloak/models/AuthenticationFlowBindings.java
new file mode 100644
index 0000000..2515472
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/models/AuthenticationFlowBindings.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models;
+
+/**
+ * Defines constants for authentication flow bindings. Strings used for lookup
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface AuthenticationFlowBindings {
+ String BROWSER_BINDING = "browser";
+ String DIRECT_GRANT_BINDING = "direct_grant";
+}
diff --git a/server-spi/src/main/java/org/keycloak/models/ClientModel.java b/server-spi/src/main/java/org/keycloak/models/ClientModel.java
index 5f4403f..aa406cf 100755
--- a/server-spi/src/main/java/org/keycloak/models/ClientModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/ClientModel.java
@@ -118,6 +118,18 @@ public interface ClientModel extends RoleContainerModel, ProtocolMapperContaine
String getAttribute(String name);
Map<String, String> getAttributes();
+ /**
+ * Get authentication flow binding override for this client. Allows client to override an authentication flow binding.
+ *
+ * @param binding examples are "browser", "direct_grant"
+ *
+ * @return
+ */
+ public String getAuthenticationFlowBindingOverride(String binding);
+ public Map<String, String> getAuthenticationFlowBindingOverrides();
+ public void removeAuthenticationFlowBindingOverride(String binding);
+ public void setAuthenticationFlowBindingOverride(String binding, String flowId);
+
boolean isFrontchannelLogout();
void setFrontchannelLogout(boolean flag);
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/AuthenticationFlowResolver.java b/server-spi-private/src/main/java/org/keycloak/models/utils/AuthenticationFlowResolver.java
new file mode 100644
index 0000000..1a27dd8
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/AuthenticationFlowResolver.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models.utils;
+
+import org.keycloak.models.AuthenticationFlowBindings;
+import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ModelException;
+import org.keycloak.sessions.AuthenticationSessionModel;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AuthenticationFlowResolver {
+
+ public static AuthenticationFlowModel resolveBrowserFlow(AuthenticationSessionModel authSession) {
+ AuthenticationFlowModel flow = null;
+ ClientModel client = authSession.getClient();
+ String clientFlow = client.getAuthenticationFlowBindingOverride(AuthenticationFlowBindings.BROWSER_BINDING);
+ if (clientFlow != null) {
+ flow = authSession.getRealm().getAuthenticationFlowById(clientFlow);
+ if (flow == null) {
+ throw new ModelException("Client " + client.getClientId() + " has browser flow override, but this flow does not exist");
+ }
+ return flow;
+ }
+ return authSession.getRealm().getBrowserFlow();
+ }
+ public static AuthenticationFlowModel resolveDirectGrantFlow(AuthenticationSessionModel authSession) {
+ AuthenticationFlowModel flow = null;
+ ClientModel client = authSession.getClient();
+ String clientFlow = client.getAuthenticationFlowBindingOverride(AuthenticationFlowBindings.DIRECT_GRANT_BINDING);
+ if (clientFlow != null) {
+ flow = authSession.getRealm().getAuthenticationFlowById(clientFlow);
+ if (flow == null) {
+ throw new ModelException("Client " + client.getClientId() + " has direct grant flow override, but this flow does not exist");
+ }
+ return flow;
+ }
+ return authSession.getRealm().getDirectGrantFlow();
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index aa6b42c..0dad16a 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -494,6 +494,7 @@ public class ModelToRepresentation {
rep.setFrontchannelLogout(clientModel.isFrontchannelLogout());
rep.setProtocol(clientModel.getProtocol());
rep.setAttributes(clientModel.getAttributes());
+ rep.setAuthenticationFlowBindingOverrides(clientModel.getAuthenticationFlowBindingOverrides());
rep.setFullScopeAllowed(clientModel.isFullScopeAllowed());
rep.setBearerOnly(clientModel.isBearerOnly());
rep.setConsentRequired(clientModel.isConsentRequired());
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 34ee766..5fb5003 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -1084,6 +1084,17 @@ public class RepresentationToModel {
}
+ if (resourceRep.getAuthenticationFlowBindingOverrides() != null) {
+ for (Map.Entry<String, String> entry : resourceRep.getAuthenticationFlowBindingOverrides().entrySet()) {
+ if (entry.getValue() == null || entry.getValue().trim().equals("")) {
+ continue;
+ } else {
+ client.setAuthenticationFlowBindingOverride(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+
if (resourceRep.getRedirectUris() != null) {
for (String redirectUri : resourceRep.getRedirectUris()) {
client.addRedirectUri(redirectUri);
@@ -1201,6 +1212,22 @@ public class RepresentationToModel {
resource.setAttribute(entry.getKey(), entry.getValue());
}
}
+ if (rep.getAttributes() != null) {
+ for (Map.Entry<String, String> entry : rep.getAttributes().entrySet()) {
+ resource.setAttribute(entry.getKey(), entry.getValue());
+ }
+ }
+
+ if (rep.getAuthenticationFlowBindingOverrides() != null) {
+ for (Map.Entry<String, String> entry : rep.getAuthenticationFlowBindingOverrides().entrySet()) {
+ if (entry.getValue() == null || entry.getValue().trim().equals("")) {
+ resource.removeAuthenticationFlowBindingOverride(entry.getKey());
+ } else {
+ resource.setAuthenticationFlowBindingOverride(entry.getKey(), entry.getValue());
+
+ }
+ }
+ }
if (rep.getNotBefore() != null) {
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 7a3c3af..4464ff7 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -38,6 +38,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.AuthenticationFlowResolver;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.LoginProtocol;
@@ -646,7 +647,7 @@ public class AuthenticationProcessor {
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setAuthenticationSession(clone)
.setFlowPath(LoginActionsService.AUTHENTICATE_PATH)
- .setFlowId(realm.getBrowserFlow().getId())
+ .setFlowId(AuthenticationFlowResolver.resolveBrowserFlow(clone).getId())
.setForwardedErrorMessage(reset.getErrorMessage())
.setForwardedSuccessMessage(reset.getSuccessMessage())
.setConnection(connection)
diff --git a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
index dbed381..619a862 100755
--- a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
+++ b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
@@ -30,6 +30,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.AuthenticationFlowResolver;
import org.keycloak.protocol.LoginProtocol.Error;
import org.keycloak.services.ErrorPageException;
import org.keycloak.services.managers.AuthenticationManager;
@@ -107,7 +108,7 @@ public abstract class AuthorizationEndpointBase {
* @return response to be returned to the browser
*/
protected Response handleBrowserAuthenticationRequest(AuthenticationSessionModel authSession, LoginProtocol protocol, boolean isPassive, boolean redirectToAuthentication) {
- AuthenticationFlowModel flow = getAuthenticationFlow();
+ AuthenticationFlowModel flow = getAuthenticationFlow(authSession);
String flowId = flow.getId();
AuthenticationProcessor processor = createProcessor(authSession, flowId, LoginActionsService.AUTHENTICATE_PATH);
event.detail(Details.CODE_ID, authSession.getParentSession().getId());
@@ -149,8 +150,8 @@ public abstract class AuthorizationEndpointBase {
}
}
- protected AuthenticationFlowModel getAuthenticationFlow() {
- return realm.getBrowserFlow();
+ protected AuthenticationFlowModel getAuthenticationFlow(AuthenticationSessionModel authSession) {
+ return AuthenticationFlowResolver.resolveBrowserFlow(authSession);
}
protected void checkSsl() {
diff --git a/services/src/main/java/org/keycloak/protocol/docker/DockerEndpoint.java b/services/src/main/java/org/keycloak/protocol/docker/DockerEndpoint.java
index 0c9cb79..6ed777d 100644
--- a/services/src/main/java/org/keycloak/protocol/docker/DockerEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/docker/DockerEndpoint.java
@@ -88,7 +88,7 @@ public class DockerEndpoint extends AuthorizationEndpointBase {
}
@Override
- protected AuthenticationFlowModel getAuthenticationFlow() {
+ protected AuthenticationFlowModel getAuthenticationFlow(AuthenticationSessionModel authSession) {
return realm.getDockerAuthenticationFlow();
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index ee0ff85..3be9686 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -56,6 +56,7 @@ import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.AuthenticationFlowResolver;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.TokenManager;
@@ -491,7 +492,7 @@ public class TokenEndpoint {
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
authSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
- AuthenticationFlowModel flow = realm.getDirectGrantFlow();
+ AuthenticationFlowModel flow = AuthenticationFlowResolver.resolveDirectGrantFlow(authSession);
String flowId = flow.getId();
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setAuthenticationSession(authSession)
diff --git a/services/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java b/services/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java
index c0be2ba..cb0c367 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java
@@ -147,7 +147,7 @@ public class SamlEcpProfileService extends SamlService {
}
@Override
- protected AuthenticationFlowModel getAuthenticationFlow() {
+ protected AuthenticationFlowModel getAuthenticationFlow(AuthenticationSessionModel authSession) {
for (AuthenticationFlowModel flowModel : realm.getAuthenticationFlows()) {
if (flowModel.getAlias().equals(DefaultAuthenticationFlows.SAML_ECP_FLOW)) {
return flowModel;
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index 33a982f..c8e3fda 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -55,6 +55,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.AuthenticationFlowResolver;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.LoginProtocolFactory;
@@ -1121,7 +1122,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
protected Response browserAuthentication(AuthenticationSessionModel authSession, String errorMessage) {
this.event.event(EventType.LOGIN);
- AuthenticationFlowModel flow = realmModel.getBrowserFlow();
+ AuthenticationFlowModel flow = AuthenticationFlowResolver.resolveBrowserFlow(authSession);
String flowId = flow.getId();
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setAuthenticationSession(authSession)
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index f09cfbb..fd171ee 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -53,6 +53,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.AuthenticationFlowResolver;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.models.utils.SystemClientUtil;
import org.keycloak.protocol.AuthorizationEndpointBase;
@@ -252,7 +253,7 @@ public class LoginActionsService {
}
protected Response processAuthentication(boolean action, String execution, AuthenticationSessionModel authSession, String errorMessage) {
- return processFlow(action, execution, authSession, AUTHENTICATE_PATH, realm.getBrowserFlow(), errorMessage, new AuthenticationProcessor());
+ return processFlow(action, execution, authSession, AUTHENTICATE_PATH, AuthenticationFlowResolver.resolveBrowserFlow(authSession), errorMessage, new AuthenticationProcessor());
}
protected Response processFlow(boolean action, String execution, AuthenticationSessionModel authSession, String flowPath, AuthenticationFlowModel flow, String errorMessage, AuthenticationProcessor processor) {
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/forms/UsernameOnlyAuthenticator.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/forms/UsernameOnlyAuthenticator.java
new file mode 100644
index 0000000..1fd72f4
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/forms/UsernameOnlyAuthenticator.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.forms;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.AuthenticationFlowContext;
+import org.keycloak.authentication.AuthenticationFlowError;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.provider.ProviderConfigProperty;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UsernameOnlyAuthenticator implements Authenticator, AuthenticatorFactory {
+ public static final String PROVIDER_ID = "testsuite-username";
+
+ @Override
+ public void authenticate(AuthenticationFlowContext context) {
+ String username = context.getHttpRequest().getDecodedFormParameters().getFirst("username");
+ UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
+ if (user == null) {
+ context.failure(AuthenticationFlowError.UNKNOWN_USER);
+ return;
+ }
+ context.setUser(user);
+ context.success();
+ }
+
+ @Override
+ public boolean requiresUser() {
+ return false;
+ }
+
+ @Override
+ public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
+ return true;
+ }
+
+ @Override
+ public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
+
+ }
+
+ @Override
+ public void action(AuthenticationFlowContext context) {
+
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "Testsuite Username Only";
+ }
+
+ @Override
+ public String getReferenceCategory() {
+ return null;
+ }
+
+ @Override
+ public boolean isConfigurable() {
+ return false;
+ }
+
+ public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
+ AuthenticationExecutionModel.Requirement.REQUIRED
+ };
+
+ @Override
+ public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
+ return REQUIREMENT_CHOICES;
+ }
+
+ @Override
+ public boolean isUserSetupAllowed() {
+ return false;
+ }
+
+ @Override
+ public String getHelpText() {
+ return "Testsuite Username authenticator. Username parameter sets username";
+ }
+
+ @Override
+ public List<ProviderConfigProperty> getConfigProperties() {
+ return null;
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public Authenticator create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
index 18317d0..3b28d99 100755
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
@@ -19,4 +19,5 @@ org.keycloak.testsuite.forms.PassThroughAuthenticator
org.keycloak.testsuite.forms.PassThroughRegistration
org.keycloak.testsuite.forms.ClickThroughAuthenticator
org.keycloak.testsuite.authentication.ExpectedParamAuthenticatorFactory
-org.keycloak.testsuite.authentication.PushButtonAuthenticatorFactory
\ No newline at end of file
+org.keycloak.testsuite.authentication.PushButtonAuthenticatorFactory
+org.keycloak.testsuite.forms.UsernameOnlyAuthenticator
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java
index f55e90f..1fac71a 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java
@@ -180,6 +180,8 @@ public class ProvidersTest extends AbstractAuthenticationTest {
"Testsuite Dummy authenticator. Just passes through and is hardcoded to a specific user");
addProviderInfo(result, "testsuite-dummy-registration", "Testsuite Dummy Pass Thru",
"Testsuite Dummy authenticator. Just passes through and is hardcoded to a specific user");
+ addProviderInfo(result, "testsuite-username", "Testsuite Username Only",
+ "Testsuite Username authenticator. Username parameter sets username");
return result;
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/FlowOverrideTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/FlowOverrideTest.java
new file mode 100644
index 0000000..33bb259
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/FlowOverrideTest.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.forms;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
+import org.keycloak.events.Details;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticationFlowBindings;
+import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.authentication.PushButtonAuthenticatorFactory;
+import org.keycloak.testsuite.pages.AppPage;
+import org.keycloak.testsuite.pages.ErrorPage;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+import org.keycloak.util.BasicAuthHelper;
+import org.openqa.selenium.By;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test that clients can override auth flows
+ *
+ * @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
+ */
+public class FlowOverrideTest extends AbstractTestRealmKeycloakTest {
+
+ public static final String TEST_APP_DIRECT_OVERRIDE = "test-app-direct-override";
+ public static final String TEST_APP_FLOW = "test-app-flow";
+ @Rule
+ public AssertEvents events = new AssertEvents(this);
+
+ @Page
+ protected AppPage appPage;
+
+ @Page
+ protected LoginPage loginPage;
+
+ @Page
+ protected ErrorPage errorPage;
+
+ @Override
+ public void configureTestRealm(RealmRepresentation testRealm) {
+ }
+
+ @Deployment
+ public static WebArchive deploy() {
+ return RunOnServerDeployment.create(UserResource.class)
+ .addPackages(true, "org.keycloak.testsuite");
+ }
+
+
+ @Before
+ public void setupFlows() {
+ testingClient.server().run(session -> {
+ RealmModel realm = session.realms().getRealmByName("test");
+
+ ClientModel client = session.realms().getClientByClientId("test-app-flow", realm);
+ if (client != null) {
+ return;
+ }
+
+ client = session.realms().getClientByClientId("test-app", realm);
+ client.setDirectAccessGrantsEnabled(true);
+
+
+
+ // Parent flow
+ AuthenticationFlowModel browser = new AuthenticationFlowModel();
+ browser.setAlias("parent-flow");
+ browser.setDescription("browser based authentication");
+ browser.setProviderId("basic-flow");
+ browser.setTopLevel(true);
+ browser.setBuiltIn(true);
+ browser = realm.addAuthenticationFlow(browser);
+
+ // Subflow2
+ AuthenticationFlowModel subflow2 = new AuthenticationFlowModel();
+ subflow2.setTopLevel(false);
+ subflow2.setBuiltIn(true);
+ subflow2.setAlias("subflow-2");
+ subflow2.setDescription("username+password AND pushButton");
+ subflow2.setProviderId("basic-flow");
+ subflow2 = realm.addAuthenticationFlow(subflow2);
+
+ AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
+ execution.setParentFlow(browser.getId());
+ execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
+ execution.setFlowId(subflow2.getId());
+ execution.setPriority(20);
+ execution.setAuthenticatorFlow(true);
+ realm.addAuthenticatorExecution(execution);
+
+ // Subflow2 - push the button
+ execution = new AuthenticationExecutionModel();
+ execution.setParentFlow(subflow2.getId());
+ execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+ execution.setAuthenticator(PushButtonAuthenticatorFactory.PROVIDER_ID);
+ execution.setPriority(10);
+ execution.setAuthenticatorFlow(false);
+
+ realm.addAuthenticatorExecution(execution);
+
+ // Subflow2 - username-password
+ execution = new AuthenticationExecutionModel();
+ execution.setParentFlow(subflow2.getId());
+ execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+ execution.setAuthenticator(UsernamePasswordFormFactory.PROVIDER_ID);
+ execution.setPriority(20);
+ execution.setAuthenticatorFlow(false);
+
+ realm.addAuthenticatorExecution(execution);
+
+ client = realm.addClient(TEST_APP_FLOW);
+ client.setSecret("password");
+ client.setBaseUrl("http://localhost:8180/auth/realms/master/app/auth");
+ client.setManagementUrl("http://localhost:8180/auth/realms/master/app/admin");
+ client.setEnabled(true);
+ client.addRedirectUri("http://localhost:8180/auth/realms/master/app/auth/*");
+ client.setAuthenticationFlowBindingOverride(AuthenticationFlowBindings.BROWSER_BINDING, browser.getId());
+ client.setPublicClient(false);
+
+ // Parent flow
+ AuthenticationFlowModel directGrant = new AuthenticationFlowModel();
+ directGrant.setAlias("direct-override-flow");
+ directGrant.setDescription("direct grant based authentication");
+ directGrant.setProviderId("basic-flow");
+ directGrant.setTopLevel(true);
+ directGrant.setBuiltIn(true);
+ directGrant = realm.addAuthenticationFlow(directGrant);
+
+ execution = new AuthenticationExecutionModel();
+ execution.setParentFlow(directGrant.getId());
+ execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+ execution.setAuthenticator(UsernameOnlyAuthenticator.PROVIDER_ID);
+ execution.setPriority(10);
+ execution.setAuthenticatorFlow(false);
+
+ realm.addAuthenticatorExecution(execution);
+
+ client = realm.addClient(TEST_APP_DIRECT_OVERRIDE);
+ client.setSecret("password");
+ client.setBaseUrl("http://localhost:8180/auth/realms/master/app/auth");
+ client.setManagementUrl("http://localhost:8180/auth/realms/master/app/admin");
+ client.setEnabled(true);
+ client.addRedirectUri("http://localhost:8180/auth/realms/master/app/auth/*");
+ client.setPublicClient(false);
+ client.setDirectAccessGrantsEnabled(true);
+ client.setAuthenticationFlowBindingOverride(AuthenticationFlowBindings.BROWSER_BINDING, browser.getId());
+ client.setAuthenticationFlowBindingOverride(AuthenticationFlowBindings.DIRECT_GRANT_BINDING, directGrant.getId());
+
+
+
+ });
+ }
+
+ //@Test
+ public void testRunConsole() throws Exception {
+ Thread.sleep(10000000);
+ }
+
+
+ @Test
+ public void testWithClientBrowserOverride() throws Exception {
+ oauth.clientId(TEST_APP_FLOW);
+ String loginFormUrl = oauth.getLoginFormUrl();
+ log.info("loginFormUrl: " + loginFormUrl);
+
+ //Thread.sleep(10000000);
+
+ driver.navigate().to(loginFormUrl);
+
+ Assert.assertEquals("PushTheButton", driver.getTitle());
+
+ // Push the button. I am redirected to username+password form
+ driver.findElement(By.name("submit1")).click();
+
+
+ loginPage.assertCurrent();
+
+ // Fill username+password. I am successfully authenticated
+ oauth.fillLoginForm("test-user@localhost", "password");
+ appPage.assertCurrent();
+
+ events.expectLogin().client("test-app-flow").detail(Details.USERNAME, "test-user@localhost").assertEvent();
+ }
+
+ @Test
+ public void testNoOverrideBrowser() throws Exception {
+ String clientId = "test-app";
+ testNoOverrideBrowser(clientId);
+ }
+
+ private void testNoOverrideBrowser(String clientId) {
+ oauth.clientId(clientId);
+ String loginFormUrl = oauth.getLoginFormUrl();
+ log.info("loginFormUrl: " + loginFormUrl);
+
+ //Thread.sleep(10000000);
+
+ driver.navigate().to(loginFormUrl);
+
+ loginPage.assertCurrent();
+
+ // Fill username+password. I am successfully authenticated
+ oauth.fillLoginForm("test-user@localhost", "password");
+ appPage.assertCurrent();
+
+ events.expectLogin().client(clientId).detail(Details.USERNAME, "test-user@localhost").assertEvent();
+ }
+
+ @Test
+ public void testGrantAccessTokenNoOverride() throws Exception {
+ testDirectGrantNoOverride("test-app");
+ }
+
+ private void testDirectGrantNoOverride(String clientId) {
+ Client httpClient = javax.ws.rs.client.ClientBuilder.newClient();
+ String grantUri = oauth.getResourceOwnerPasswordCredentialGrantUrl();
+ WebTarget grantTarget = httpClient.target(grantUri);
+
+ { // test no password
+ String header = BasicAuthHelper.createHeader(clientId, "password");
+ Form form = new Form();
+ form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
+ form.param("username", "test-user@localhost");
+ Response response = grantTarget.request()
+ .header(HttpHeaders.AUTHORIZATION, header)
+ .post(Entity.form(form));
+ assertEquals(401, response.getStatus());
+ response.close();
+ }
+
+ { // test invalid password
+ String header = BasicAuthHelper.createHeader(clientId, "password");
+ Form form = new Form();
+ form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
+ form.param("username", "test-user@localhost");
+ form.param("password", "invalid");
+ Response response = grantTarget.request()
+ .header(HttpHeaders.AUTHORIZATION, header)
+ .post(Entity.form(form));
+ assertEquals(401, response.getStatus());
+ response.close();
+ }
+
+ { // test valid password
+ String header = BasicAuthHelper.createHeader(clientId, "password");
+ Form form = new Form();
+ form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
+ form.param("username", "test-user@localhost");
+ form.param("password", "password");
+ Response response = grantTarget.request()
+ .header(HttpHeaders.AUTHORIZATION, header)
+ .post(Entity.form(form));
+ assertEquals(200, response.getStatus());
+ response.close();
+ }
+
+ httpClient.close();
+ events.clear();
+ }
+
+ @Test
+ public void testGrantAccessTokenWithClientOverride() throws Exception {
+ String clientId = TEST_APP_DIRECT_OVERRIDE;
+ Client httpClient = javax.ws.rs.client.ClientBuilder.newClient();
+ String grantUri = oauth.getResourceOwnerPasswordCredentialGrantUrl();
+ WebTarget grantTarget = httpClient.target(grantUri);
+
+ { // test no password
+ String header = BasicAuthHelper.createHeader(clientId, "password");
+ Form form = new Form();
+ form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
+ form.param("username", "test-user@localhost");
+ Response response = grantTarget.request()
+ .header(HttpHeaders.AUTHORIZATION, header)
+ .post(Entity.form(form));
+ assertEquals(200, response.getStatus());
+ response.close();
+ }
+
+ httpClient.close();
+ events.clear();
+ }
+
+ @Test
+ public void testRestInterface() throws Exception {
+ ClientsResource clients = adminClient.realm("test").clients();
+ List<ClientRepresentation> query = clients.findByClientId(TEST_APP_DIRECT_OVERRIDE);
+ ClientRepresentation clientRep = query.get(0);
+ String directGrantFlowId = clientRep.getAuthenticationFlowBindingOverrides().get(AuthenticationFlowBindings.DIRECT_GRANT_BINDING);
+ Assert.assertNotNull(directGrantFlowId);
+ clientRep.getAuthenticationFlowBindingOverrides().put(AuthenticationFlowBindings.DIRECT_GRANT_BINDING, "");
+ clients.get(clientRep.getId()).update(clientRep);
+ testDirectGrantNoOverride(TEST_APP_DIRECT_OVERRIDE);
+ clientRep.getAuthenticationFlowBindingOverrides().put(AuthenticationFlowBindings.DIRECT_GRANT_BINDING, directGrantFlowId);
+ clients.get(clientRep.getId()).update(clientRep);
+ testGrantAccessTokenWithClientOverride();
+
+ query = clients.findByClientId(TEST_APP_FLOW);
+ clientRep = query.get(0);
+ String browserFlowId = clientRep.getAuthenticationFlowBindingOverrides().get(AuthenticationFlowBindings.BROWSER_BINDING);
+ Assert.assertNotNull(browserFlowId);
+ clientRep.getAuthenticationFlowBindingOverrides().put(AuthenticationFlowBindings.BROWSER_BINDING, "");
+ clients.get(clientRep.getId()).update(clientRep);
+ testNoOverrideBrowser(TEST_APP_FLOW);
+ clientRep.getAuthenticationFlowBindingOverrides().put(AuthenticationFlowBindings.BROWSER_BINDING, browserFlowId);
+ clients.get(clientRep.getId()).update(clientRep);
+ testWithClientBrowserOverride();
+
+ }
+
+}