Allow to configure if users are automatically redirected when the email domain matches an organization
Closes #30050 Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
parent
c99cddb0de
commit
4c39fcc79d
6 changed files with 78 additions and 10 deletions
|
@ -3192,3 +3192,5 @@ linkUpdateError=Could not update link to identity provider\: {{error}}
|
|||
noResultsFound=No results found
|
||||
linkedOrganization=Linked organization
|
||||
send=Send
|
||||
redirectWhenEmailMatches=Redirect when email domain matches
|
||||
redirectWhenEmailMatchesHelp=Automatically redirect the user to this identity provider when the email domain matches the domain
|
|
@ -145,6 +145,14 @@ export const LinkIdentityProviderModal = ({
|
|||
labelIcon={t("shownOnLoginPageHelp")}
|
||||
stringify
|
||||
/>
|
||||
<DefaultSwitchControl
|
||||
name={convertAttributeNameToForm(
|
||||
"config.kc.org.broker.redirect.mode.email-matches",
|
||||
)}
|
||||
label={t("redirectWhenEmailMatches")}
|
||||
labelIcon={t("redirectWhenEmailMatchesHelp")}
|
||||
stringify
|
||||
/>
|
||||
</Form>
|
||||
</FormProvider>
|
||||
</Modal>
|
||||
|
|
|
@ -29,6 +29,24 @@ public interface OrganizationModel {
|
|||
String ORGANIZATION_DOMAIN_ATTRIBUTE = "kc.org.domain";
|
||||
String BROKER_PUBLIC = "kc.org.broker.public";
|
||||
|
||||
enum IdentityProviderRedirectMode {
|
||||
EMAIL_MATCH("kc.org.broker.redirect.mode.email-matches");
|
||||
|
||||
private final String key;
|
||||
|
||||
IdentityProviderRedirectMode(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public boolean isSet(IdentityProviderModel broker) {
|
||||
return Boolean.parseBoolean(broker.getConfig().get(key));
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
String getId();
|
||||
|
||||
void setName(String name);
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.keycloak.http.HttpRequest;
|
|||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.OrganizationModel;
|
||||
import org.keycloak.models.OrganizationModel.IdentityProviderRedirectMode;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.FormMessage;
|
||||
|
@ -109,14 +110,8 @@ public class OrganizationAuthenticator extends IdentityProviderAuthenticator {
|
|||
|
||||
List<IdentityProviderModel> brokers = organization.getIdentityProviders().toList();
|
||||
|
||||
for (IdentityProviderModel broker : brokers) {
|
||||
String idpDomain = broker.getConfig().get(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
|
||||
|
||||
if (emailDomain.equals(idpDomain)) {
|
||||
// redirect the user using the broker that matches the email domain
|
||||
redirect(context, broker.getAlias(), username);
|
||||
return;
|
||||
}
|
||||
if (redirect(context, brokers, username, emailDomain)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasPublicBrokers(brokers)) {
|
||||
|
@ -195,4 +190,20 @@ public class OrganizationAuthenticator extends IdentityProviderAuthenticator {
|
|||
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
return realm.isOrganizationsEnabled();
|
||||
}
|
||||
|
||||
protected boolean redirect(AuthenticationFlowContext context, List<IdentityProviderModel> brokers, String username, String emailDomain) {
|
||||
for (IdentityProviderModel broker : brokers) {
|
||||
if (IdentityProviderRedirectMode.EMAIL_MATCH.isSet(broker)) {
|
||||
String idpDomain = broker.getConfig().get(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
|
||||
|
||||
if (emailDomain.equals(idpDomain)) {
|
||||
// redirect the user using the broker that matches the email domain
|
||||
redirect(context, broker.getAlias(), username);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.keycloak.admin.client.resource.OrganizationResource;
|
|||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.OrganizationModel;
|
||||
import org.keycloak.models.OrganizationModel.IdentityProviderRedirectMode;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
|
@ -498,6 +499,32 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
|
|||
assertEquals(bc.getIDPAlias(), federatedIdentities.get(0).getIdentityProvider());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoNotRedirectToIdentityProviderAssociatedWithOrganizationDomain() {
|
||||
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||
IdentityProviderRepresentation idp = organization.identityProviders().get(bc.getIDPAlias()).toRepresentation();
|
||||
idp.getConfig().put(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE, "neworg.org");
|
||||
idp.getConfig().put(IdentityProviderRedirectMode.EMAIL_MATCH.getKey(), Boolean.FALSE.toString());
|
||||
idp.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
|
||||
testRealm().identityProviders().get(bc.getIDPAlias()).update(idp);
|
||||
|
||||
oauth.clientId("broker-app");
|
||||
loginPage.open(bc.consumerRealmName());
|
||||
log.debug("Logging in");
|
||||
Assert.assertFalse(loginPage.isPasswordInputPresent());
|
||||
Assert.assertFalse(loginPage.isSocialButtonPresent(bc.getIDPAlias()));
|
||||
Assert.assertFalse(loginPage.isSocialButtonPresent(idp.getAlias()));
|
||||
loginPage.loginUsername(bc.getUserEmail());
|
||||
|
||||
// user not automatically redirected to the organization identity provider
|
||||
waitForPage(driver, "sign in to", true);
|
||||
Assert.assertTrue("Driver should be on the consumer realm page right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
|
||||
Assert.assertFalse(loginPage.isPasswordInputPresent());
|
||||
Assert.assertTrue(driver.getPageSource().contains("Your email domain matches the " + organizationName + " organization but you don't have an account yet."));
|
||||
Assert.assertTrue(loginPage.isSocialButtonPresent(bc.getIDPAlias()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoginUsingBrokerWithoutDomain() {
|
||||
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.jboss.arquillian.graphene.page.Page;
|
|||
import org.keycloak.admin.client.resource.OrganizationResource;
|
||||
import org.keycloak.models.OrganizationModel;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.models.OrganizationModel.IdentityProviderRedirectMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
|
@ -117,6 +118,7 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
|
|||
}
|
||||
// set the idp domain to the first domain used to create the org.
|
||||
broker.getConfig().put(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE, orgDomains[0]);
|
||||
broker.getConfig().put(IdentityProviderRedirectMode.EMAIL_MATCH.getKey(), Boolean.TRUE.toString());
|
||||
testRealm.identityProviders().create(broker).close();
|
||||
testCleanup.addCleanup(testRealm.identityProviders().get(broker.getAlias())::remove);
|
||||
testRealm.organizations().get(id).identityProviders().addIdentityProvider(broker.getAlias()).close();
|
||||
|
|
Loading…
Reference in a new issue