Closes #10616
This commit is contained in:
parent
0c25da542c
commit
a7c8aa1dd3
2 changed files with 212 additions and 0 deletions
|
@ -25,6 +25,7 @@ import org.keycloak.authentication.AuthenticationProcessor;
|
||||||
import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator;
|
import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator;
|
||||||
import org.keycloak.authentication.authenticators.broker.util.PostBrokerLoginConstants;
|
import org.keycloak.authentication.authenticators.broker.util.PostBrokerLoginConstants;
|
||||||
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
|
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
|
||||||
|
import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
|
||||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||||
|
@ -546,6 +547,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
if (federatedUser == null) {
|
if (federatedUser == null) {
|
||||||
|
|
||||||
logger.debugf("Federated user not found for provider '%s' and broker username '%s'", providerId, context.getUsername());
|
logger.debugf("Federated user not found for provider '%s' and broker username '%s'", providerId, context.getUsername());
|
||||||
|
authenticationSession.setAuthNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME, context.getUsername());
|
||||||
|
|
||||||
String username = context.getModelUsername();
|
String username = context.getModelUsername();
|
||||||
if (username == null) {
|
if (username == null) {
|
||||||
|
@ -592,6 +594,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
return Response.status(302).location(redirect).build();
|
return Response.status(302).location(redirect).build();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
authenticationSession.setAuthNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME, federatedUser.getUsername());
|
||||||
Response response = validateUser(authenticationSession, federatedUser, realmModel);
|
Response response = validateUser(authenticationSession, federatedUser, realmModel);
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
return response;
|
return response;
|
||||||
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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.broker;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
import org.keycloak.events.Details;
|
||||||
|
import org.keycloak.events.EventType;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple test to check the events after a broker login using OIDC. It also
|
||||||
|
* tests that the event username is not wrong after a form login error
|
||||||
|
* (Issue #10616).
|
||||||
|
*
|
||||||
|
* @author rmartinc
|
||||||
|
*/
|
||||||
|
public final class KcOidcBrokerEventTest extends AbstractBrokerTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public AssertEvents events = new AssertEvents(this);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BrokerConfiguration getBrokerConfiguration() {
|
||||||
|
return KcOidcBrokerConfiguration.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkLoginEvents(RealmResource providerRealm, RealmResource consumerRealm, String providerUserId, String consumerUserId) {
|
||||||
|
events.expect(EventType.LOGIN)
|
||||||
|
.realm(providerRealm.toRepresentation().getId())
|
||||||
|
.user(providerUserId)
|
||||||
|
.client(bc.getIDPClientIdInProviderRealm())
|
||||||
|
.session(Matchers.any(String.class))
|
||||||
|
.detail(Details.USERNAME, bc.getUserLogin())
|
||||||
|
.assertEvent();
|
||||||
|
|
||||||
|
events.expect(EventType.CODE_TO_TOKEN)
|
||||||
|
.session(Matchers.any(String.class))
|
||||||
|
.realm(providerRealm.toRepresentation().getId())
|
||||||
|
.user(providerUserId)
|
||||||
|
.client(bc.getIDPClientIdInProviderRealm())
|
||||||
|
.assertEvent();
|
||||||
|
|
||||||
|
events.expect(EventType.USER_INFO_REQUEST)
|
||||||
|
.session(Matchers.any(String.class))
|
||||||
|
.realm(providerRealm.toRepresentation().getId())
|
||||||
|
.user(providerUserId)
|
||||||
|
.client(bc.getIDPClientIdInProviderRealm())
|
||||||
|
.assertEvent();
|
||||||
|
|
||||||
|
events.expect(EventType.LOGIN)
|
||||||
|
.realm(consumerRealm.toRepresentation().getId())
|
||||||
|
.client("account")
|
||||||
|
.user(consumerUserId == null? Matchers.any(String.class) : Matchers.is(consumerUserId))
|
||||||
|
.session(Matchers.any(String.class))
|
||||||
|
.detail(Details.USERNAME, bc.getUserLogin())
|
||||||
|
.detail(Details.IDENTITY_PROVIDER_USERNAME, bc.getUserLogin())
|
||||||
|
.detail(Details.IDENTITY_PROVIDER, bc.getIDPAlias())
|
||||||
|
.assertEvent();
|
||||||
|
|
||||||
|
events.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doALoginError(RealmResource consumerRealm) {
|
||||||
|
events.clear();
|
||||||
|
|
||||||
|
// navigate to the account url of the consumer realm
|
||||||
|
driver.navigate().to(getAccountUrl(BrokerTestTools.getConsumerRoot(), bc.consumerRealmName()));
|
||||||
|
|
||||||
|
// Do a wrong login with a user that does not exist
|
||||||
|
loginPage.login("wrong-user", "wrong-password");
|
||||||
|
|
||||||
|
events.expect(EventType.LOGIN_ERROR)
|
||||||
|
.realm(consumerRealm.toRepresentation().getId())
|
||||||
|
.user((String) null)
|
||||||
|
.client("account")
|
||||||
|
.session((String) null)
|
||||||
|
.detail(Details.USERNAME, "wrong-user")
|
||||||
|
.error("user_not_found")
|
||||||
|
.assertEvent();
|
||||||
|
|
||||||
|
events.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loginUser() {
|
||||||
|
RealmResource providerRealm = adminClient.realm(bc.providerRealmName());
|
||||||
|
RealmResource consumerRealm = adminClient.realm(bc.consumerRealmName());
|
||||||
|
UserRepresentation providerUser = providerRealm.users().search(bc.getUserLogin()).iterator().next();
|
||||||
|
events.clear();
|
||||||
|
|
||||||
|
super.loginUser();
|
||||||
|
|
||||||
|
checkLoginEvents(providerRealm, consumerRealm, providerUser.getId(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loginUserAfterError() {
|
||||||
|
RealmResource providerRealm = adminClient.realm(bc.providerRealmName());
|
||||||
|
RealmResource consumerRealm = adminClient.realm(bc.consumerRealmName());
|
||||||
|
UserRepresentation providerUser = providerRealm.users().search(bc.getUserLogin()).iterator().next();
|
||||||
|
|
||||||
|
doALoginError(consumerRealm);
|
||||||
|
|
||||||
|
logInWithBroker(bc);
|
||||||
|
|
||||||
|
BrokerTestTools.waitForPage(driver, "update account information", false);
|
||||||
|
updateAccountInformationPage.assertCurrent();
|
||||||
|
Assert.assertTrue("We must be on correct realm right now",
|
||||||
|
driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
|
||||||
|
|
||||||
|
log.debug("Updating info on updateAccount page");
|
||||||
|
updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname");
|
||||||
|
|
||||||
|
List<UserRepresentation> users = consumerRealm.users().search(bc.getUserLogin());
|
||||||
|
Assert.assertEquals("There must be one user", 1, users.size());
|
||||||
|
UserRepresentation consumerUser = users.iterator().next();
|
||||||
|
Assert.assertEquals(bc.getUserEmail(), consumerUser.getEmail());
|
||||||
|
|
||||||
|
checkLoginEvents(providerRealm, consumerRealm, providerUser.getId(), consumerUser.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void testSingleLogout() {
|
||||||
|
RealmResource providerRealm = adminClient.realm(bc.providerRealmName());
|
||||||
|
UserRepresentation providerUser = providerRealm.users().search(bc.getUserLogin()).iterator().next();
|
||||||
|
events.clear();
|
||||||
|
|
||||||
|
super.testSingleLogout();
|
||||||
|
|
||||||
|
events.expect(EventType.LOGOUT)
|
||||||
|
.realm(providerRealm.toRepresentation().getId())
|
||||||
|
.user(providerUser.getId())
|
||||||
|
.client((String) null)
|
||||||
|
.session(Matchers.any(String.class))
|
||||||
|
.assertEvent();
|
||||||
|
|
||||||
|
events.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Override
|
||||||
|
public void loginWithExistingUser() {
|
||||||
|
// first login to execute the first login flow and create/link the user
|
||||||
|
testLogInAsUserInIDP();
|
||||||
|
|
||||||
|
RealmResource providerRealm = adminClient.realm(bc.providerRealmName());
|
||||||
|
RealmResource consumerRealm = adminClient.realm(bc.consumerRealmName());
|
||||||
|
UserRepresentation providerUser = providerRealm.users().search(bc.getUserLogin()).iterator().next();
|
||||||
|
UserRepresentation consumerUser = consumerRealm.users().search(bc.getUserLogin()).iterator().next();
|
||||||
|
Integer userCount = adminClient.realm(bc.consumerRealmName()).users().count();
|
||||||
|
|
||||||
|
// now do the second login
|
||||||
|
driver.navigate().to(getAccountUrl(BrokerTestTools.getConsumerRoot(), bc.consumerRealmName()));
|
||||||
|
logInWithBroker(bc);
|
||||||
|
|
||||||
|
Assert.assertEquals(accountPage.buildUri().toASCIIString().replace("master", "consumer") + "/", driver.getCurrentUrl());
|
||||||
|
Assert.assertEquals(userCount, adminClient.realm(bc.consumerRealmName()).users().count());
|
||||||
|
|
||||||
|
checkLoginEvents(providerRealm, consumerRealm, providerUser.getId(), consumerUser.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLogInAsUserInIDPAfterError() {
|
||||||
|
loginUserAfterError();
|
||||||
|
testSingleLogout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loginWithExistingUserAfterError() {
|
||||||
|
// first login to execute the first login flow and create/link the user
|
||||||
|
testLogInAsUserInIDP();
|
||||||
|
|
||||||
|
RealmResource providerRealm = adminClient.realm(bc.providerRealmName());
|
||||||
|
RealmResource consumerRealm = adminClient.realm(bc.consumerRealmName());
|
||||||
|
UserRepresentation providerUser = providerRealm.users().search(bc.getUserLogin()).iterator().next();
|
||||||
|
UserRepresentation consumerUser = consumerRealm.users().search(bc.getUserLogin()).iterator().next();
|
||||||
|
Integer userCount = adminClient.realm(bc.consumerRealmName()).users().count();
|
||||||
|
|
||||||
|
doALoginError(consumerRealm);
|
||||||
|
|
||||||
|
// now perform the login via the broker
|
||||||
|
logInWithBroker(bc);
|
||||||
|
|
||||||
|
Assert.assertEquals(accountPage.buildUri().toASCIIString().replace("master", "consumer") + "/", driver.getCurrentUrl());
|
||||||
|
Assert.assertEquals(userCount, adminClient.realm(bc.consumerRealmName()).users().count());
|
||||||
|
|
||||||
|
checkLoginEvents(providerRealm, consumerRealm, providerUser.getId(), consumerUser.getId());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue