Remove the kc.org.broker.public attribute and use hideOnLogin in the IDP instead

Closes #32209

Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Co-authored-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
Stefan Guilhen 2024-09-20 11:08:55 -03:00 committed by GitHub
parent aa9e6c730e
commit 900c496ffe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 112 additions and 84 deletions

View file

@ -3244,6 +3244,8 @@ temporaryAdmin=Temporary admin user account. Ensure it is replaced with a perman
temporaryService=Temporary admin service account. Ensure it is replaced with a permanent admin service account as soon as possible.
addOrganizationAttributes.label=Add organization attributes
addOrganizationAttributes.help=If enabled, the organization attributes will be available for each organization mapped to the token.
identityProviderUnlink=Unlink identity provider?
identityProviderUnlinkConfirm=Are you sure you want to unlink this identity provider?
disableConfirmUserTitle=Disable user?
disableConfirmUser=Are you sure you want to disable this user?
eventTypes.UPDATE_CREDENTIAL.name=Update credential

View file

@ -48,6 +48,7 @@ export const IdentityProviderSelect = ({
const {
control,
getValues,
setValue,
formState: { errors },
} = useFormContext();
const values: string[] | undefined = getValues(name!);
@ -72,8 +73,7 @@ export const IdentityProviderSelect = ({
params.search = search;
}
const idps = await adminClient.identityProviders.find(params);
return idps;
return await adminClient.identityProviders.find(params);
},
setIdps,
[search],
@ -85,7 +85,7 @@ export const IdentityProviderSelect = ({
const options = identityProviders.map((option) => (
<SelectOption
key={option!.alias}
value={option!.alias}
value={option}
selected={values?.includes(option!.alias!)}
>
{option!.alias}
@ -200,7 +200,9 @@ export const IdentityProviderSelect = ({
isOpen={open}
selected={field.value}
onSelect={(_, v) => {
const option = v?.toString();
const idp = v as IdentityProviderRepresentation;
setValue("hideOnLogin", idp.hideOnLogin);
const option = idp.alias!;
if (variant !== "typeaheadMulti") {
const removed = field.value.includes(option);

View file

@ -43,10 +43,7 @@ const ShownOnLoginPageCheck = ({
{ alias: row.alias! },
{
...row,
config: {
...row.config,
"kc.org.broker.public": `${value}`,
},
hideOnLogin: value,
},
);
addAlert(t("linkUpdatedSuccessful"));
@ -61,7 +58,7 @@ const ShownOnLoginPageCheck = ({
<Switch
label={t("on")}
labelOff={t("off")}
isChecked={row.config?.["kc.org.broker.public"] === "true"}
isChecked={row.hideOnLogin}
onChange={(_, value) => toggle(value)}
/>
);
@ -204,8 +201,8 @@ export const IdentityProviders = () => {
displayKey: "providerDetails",
},
{
name: "config['kc.org.broker.public']",
displayKey: "shownOnLoginPage",
name: "hideOnLogin",
displayKey: "hideOnLoginPage",
cellRenderer: (row) => (
<ShownOnLoginPageCheck row={row} refresh={refresh} />
),

View file

@ -29,9 +29,9 @@ type LinkIdentityProviderModalProps = {
type LinkRepresentation = {
alias: string[] | string;
hideOnLogin: boolean;
config: {
"kc.org.domain": string;
"kc.org.broker.public": string;
};
};
@ -51,7 +51,11 @@ export const LinkIdentityProviderModal = ({
useEffect(
() =>
convertToFormValues(
{ ...identityProvider, alias: [identityProvider?.alias] },
{
...identityProvider,
alias: [identityProvider?.alias],
hideOnLogin: identityProvider?.hideOnLogin,
},
setValue,
),
[],
@ -72,6 +76,7 @@ export const LinkIdentityProviderModal = ({
...foundIdentityProvider.config,
...config,
};
foundIdentityProvider.hideOnLogin = data.hideOnLogin;
await adminClient.identityProviders.update(
{ alias: data.alias[0] },
foundIdentityProvider,
@ -140,10 +145,9 @@ export const LinkIdentityProviderModal = ({
menuAppendTo="parent"
/>
<DefaultSwitchControl
name={convertAttributeNameToForm("config.kc.org.broker.public")}
label={t("shownOnLoginPage")}
labelIcon={t("shownOnLoginPageHelp")}
stringify
name="hideOnLogin"
label={t("hideOnLoginPage")}
labelIcon={t("hideOnLoginPageHelp")}
/>
<DefaultSwitchControl
name={convertAttributeNameToForm(

View file

@ -76,6 +76,27 @@ public class JpaUpdate26_0_0_IdentityProviderAttributesMigration extends CustomK
} catch (Exception e) {
throw new CustomChangeException(getTaskId() + ": Exception when updating data from previous version", e);
}
// move kc.org.broker.public from the config to the new HIDE_ON_LOGIN in the IDP.
try (PreparedStatement ps = connection.prepareStatement("SELECT c.IDENTITY_PROVIDER_ID, c.VALUE" +
" FROM " + getTableName("IDENTITY_PROVIDER_CONFIG") + " c WHERE c.NAME = 'kc.org.broker.public'");
ResultSet resultSet = ps.executeQuery()
) {
while (resultSet.next()) {
String id = resultSet.getString(1);
String value = resultSet.getString(2);
statements.add(new UpdateStatement(null, null, database.correctObjectName("IDENTITY_PROVIDER", Table.class))
.addNewColumnValue("HIDE_ON_LOGIN", !Boolean.parseBoolean(value))
.setWhereClause("INTERNAL_ID=?")
.addWhereParameter(id));
}
statements.add(new DeleteStatement(null, null, database.correctObjectName("IDENTITY_PROVIDER_CONFIG", Table.class))
.setWhere("NAME=?")
.addWhereParameter("kc.org.broker.public"));
} catch (Exception e) {
throw new CustomChangeException(getTaskId() + ": Exception when updating data from previous version", e);
}
}
@Override

View file

@ -58,6 +58,7 @@ import static org.keycloak.models.IdentityProviderModel.FIRST_BROKER_LOGIN_FLOW_
import static org.keycloak.models.IdentityProviderModel.HIDE_ON_LOGIN;
import static org.keycloak.models.IdentityProviderModel.LINK_ONLY;
import static org.keycloak.models.IdentityProviderModel.ORGANIZATION_ID;
import static org.keycloak.models.IdentityProviderModel.ORGANIZATION_ID_NOT_NULL;
import static org.keycloak.models.IdentityProviderModel.POST_BROKER_LOGIN_FLOW_ID;
import static org.keycloak.models.IdentityProviderModel.SEARCH;
import static org.keycloak.models.jpa.PaginationUtils.paginateQuery;
@ -252,6 +253,10 @@ public class JpaIdentityProviderStorageProvider implements IdentityProviderStora
}
break;
}
case ORGANIZATION_ID_NOT_NULL: {
predicates.add(builder.isNotNull(idp.get(ORGANIZATION_ID)));
break;
}
case SEARCH: {
if (StringUtil.isNotBlank(value)) {
predicates.add(this.getAliasSearchPredicate(value, builder, idp));

View file

@ -17,7 +17,6 @@
package org.keycloak.organization.jpa;
import static org.keycloak.models.OrganizationModel.BROKER_PUBLIC;
import static org.keycloak.models.OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE;
import static org.keycloak.models.jpa.PaginationUtils.paginateQuery;
import static org.keycloak.utils.StreamsUtil.closing;
@ -367,7 +366,6 @@ public class JpaOrganizationProvider implements OrganizationProvider {
// clear the organization id and any domain assigned to the IDP.
identityProvider.setOrganizationId(null);
identityProvider.getConfig().remove(ORGANIZATION_DOMAIN_ATTRIBUTE);
identityProvider.getConfig().remove(BROKER_PUBLIC);
session.identityProviders().update(identityProvider);
return true;

View file

@ -867,7 +867,7 @@ public class RepresentationToModel {
identityProviderModel.setEnabled(representation.isEnabled());
identityProviderModel.setLinkOnly(representation.isLinkOnly());
identityProviderModel.setHideOnLogin(representation.isHideOnLogin());
// check if the legacy hide on login attribute is present.
// remove the legacy hide on login attribute if present.
String hideOnLoginAttr = representation.getConfig().remove(IdentityProviderModel.LEGACY_HIDE_ON_LOGIN_ATTR);
if (hideOnLoginAttr != null) identityProviderModel.setHideOnLogin(Boolean.parseBoolean(hideOnLoginAttr));
identityProviderModel.setTrustEmail(representation.isTrustEmail());

View file

@ -50,6 +50,7 @@ public class IdentityProviderModel implements Serializable {
public static final String LOGIN_HINT = "loginHint";
public static final String METADATA_DESCRIPTOR_URL = "metadataDescriptorUrl";
public static final String ORGANIZATION_ID = "organizationId";
public static final String ORGANIZATION_ID_NOT_NULL = "organizationIdNotNull";
public static final String PASS_MAX_AGE = "passMaxAge";
public static final String POST_BROKER_LOGIN_FLOW_ID = "postBrokerLoginFlowId";
public static final String SEARCH = "search";

View file

@ -176,8 +176,9 @@ public interface IdentityProviderStorageProvider extends Provider {
if (organizationId != null) {
// we want the IDPs associated with a specific org.
searchOptions.put(IdentityProviderModel.ORGANIZATION_ID, organizationId);
} else {
searchOptions.put(IdentityProviderModel.ORGANIZATION_ID_NOT_NULL, "");
}
searchOptions.put(OrganizationModel.BROKER_PUBLIC, "true");
result = Stream.concat(result, getAllStream(searchOptions, null, null));
}
return result;
@ -251,7 +252,6 @@ public interface IdentityProviderStorageProvider extends Provider {
public static Predicate<IdentityProviderModel> getLoginPredicate() {
return ((Predicate<IdentityProviderModel>) Objects::nonNull)
.and(idp -> idp.getOrganizationId() == null || Boolean.parseBoolean(idp.getConfig().get(OrganizationModel.BROKER_PUBLIC)))
.and(Stream.of(values()).map(LoginFilter::getFilter).reduce(Predicate::and).get());
}
}

View file

@ -27,7 +27,6 @@ public interface OrganizationModel {
String ORGANIZATION_ATTRIBUTE = "kc.org";
String ORGANIZATION_NAME_ATTRIBUTE = "kc.org.name";
String ORGANIZATION_DOMAIN_ATTRIBUTE = "kc.org.domain";
String BROKER_PUBLIC = "kc.org.broker.public";
String ALIAS = "alias";
enum IdentityProviderRedirectMode {

View file

@ -71,12 +71,6 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
public SAMLIdentityProviderConfig() {
}
@Override
public void setHideOnLogin(boolean hideOnLogin) {
super.setHideOnLogin(hideOnLogin);
getConfig().put(LEGACY_HIDE_ON_LOGIN_ATTR, String.valueOf(hideOnLogin));
}
public SAMLIdentityProviderConfig(IdentityProviderModel identityProviderModel) {
super(identityProviderModel);
}

View file

@ -40,6 +40,8 @@ import org.keycloak.saml.processing.core.saml.v2.util.SAMLMetadataUtil;
import org.keycloak.saml.validators.DestinationValidator;
import org.w3c.dom.Element;
import static org.keycloak.models.IdentityProviderModel.LEGACY_HIDE_ON_LOGIN_ATTR;
/**
* @author Pedro Igor
*/
@ -160,7 +162,7 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
for (AttributeType attribute : entityType.getExtensions().getEntityAttributes().getAttribute()) {
if (MACEDIR_ENTITY_CATEGORY.equals(attribute.getName())
&& attribute.getAttributeValue().contains(REFEDS_HIDE_FROM_DISCOVERY)) {
samlIdentityProviderConfig.setHideOnLogin(true);
samlIdentityProviderConfig.getConfig().put(LEGACY_HIDE_ON_LOGIN_ATTR, String.valueOf(true));
}
}

View file

@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import jakarta.ws.rs.core.MultivaluedMap;
@ -287,7 +288,7 @@ public class OrganizationAuthenticator extends IdentityProviderAuthenticator {
}
private boolean hasPublicBrokers(OrganizationModel organization) {
return organization.getIdentityProviders().anyMatch(p -> Boolean.parseBoolean(p.getConfig().getOrDefault(OrganizationModel.BROKER_PUBLIC, Boolean.FALSE.toString())));
return organization.getIdentityProviders().anyMatch(Predicate.not(IdentityProviderModel::isHideOnLogin));
}
private OrganizationProvider getOrganizationProvider() {

View file

@ -64,8 +64,7 @@ public class OrganizationAwareIdentityProviderBean extends IdentityProviderBean
// we already have the organization, just fetch the organization's public enabled IDPs.
if (this.organization != null) {
return organization.getIdentityProviders()
.filter(idp -> idp.isEnabled() && !idp.isLinkOnly() && !idp.isHideOnLogin()
&& Boolean.parseBoolean(idp.getConfig().get(OrganizationModel.BROKER_PUBLIC)))
.filter(idp -> idp.isEnabled() && !idp.isLinkOnly() && !idp.isHideOnLogin())
.filter(idp -> !Objects.equals(existingIDP, idp.getAlias()))
.map(idp -> createIdentityProvider(super.realm, super.baseURI, idp))
.sorted(IDP_COMPARATOR_INSTANCE).toList();
@ -104,6 +103,6 @@ public class OrganizationAwareIdentityProviderBean extends IdentityProviderBean
if (organization != null && !Objects.equals(organization.getId(),idp.getOrganizationId())) {
return false;
}
return Boolean.parseBoolean(idp.getConfig().getOrDefault(OrganizationModel.BROKER_PUBLIC, Boolean.FALSE.toString()));
return !idp.isHideOnLogin();
}
}

View file

@ -1088,8 +1088,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
// import endpoint simply converts IDPSSODescriptor into key value pairs.
// check that saml-idp-metadata.xml was properly converted into key value pairs
//System.out.println(config);
assertThat(config.keySet(), containsInAnyOrder(
"syncMode",
List<String> keys = new ArrayList<>(List.of("syncMode",
"validateSignature",
"singleLogoutServiceUrl",
"postBindingLogout",
@ -1103,9 +1102,12 @@ public class IdentityProviderTest extends AbstractAdminTest {
"signingCertificate",
"addExtensionsElementWithKeyInfo",
"loginHint",
"hideOnLoginPage",
"idpEntityId"
));
if (hasHideOnLoginPage) {
keys.add("hideOnLoginPage");
}
assertThat(config.keySet(), containsInAnyOrder(keys.toArray()));
assertThat(config, hasEntry("validateSignature", "true"));
assertThat(config, hasEntry("singleLogoutServiceUrl", "http://localhost:8080/auth/realms/master/protocol/saml"));
assertThat(config, hasEntry("artifactResolutionServiceUrl", "http://localhost:8080/auth/realms/master/protocol/saml/resolve"));
@ -1116,7 +1118,9 @@ public class IdentityProviderTest extends AbstractAdminTest {
assertThat(config, hasEntry("wantAuthnRequestsSigned", "true"));
assertThat(config, hasEntry("addExtensionsElementWithKeyInfo", "false"));
assertThat(config, hasEntry("nameIDPolicyFormat", "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"));
assertThat(config, hasEntry("hideOnLoginPage", "true"));
if (hasHideOnLoginPage) {
assertThat(config, hasEntry("hideOnLoginPage", "true"));
}
assertThat(config, hasEntry("idpEntityId", "http://localhost:8080/auth/realms/master"));
assertThat(config, hasEntry(is("signingCertificate"), notNullValue()));
}

View file

@ -121,9 +121,9 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
return createOrganization(realm, getCleanup(), name, brokerConfigFunction.apply(name).setUpIdentityProvider(), orgDomains);
}
protected OrganizationRepresentation createOrganization(String name, Map<String, String> brokerConfig) {
protected OrganizationRepresentation createOrganization(String name, boolean isBrokerPublic) {
IdentityProviderRepresentation broker = brokerConfigFunction.apply(name).setUpIdentityProvider();
broker.getConfig().putAll(brokerConfig);
broker.setHideOnLogin(!isBrokerPublic);
return createOrganization(testRealm(), getCleanup(), name, broker, name + ".org");
}

View file

@ -107,7 +107,7 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
public void testIdentityFirstUserNotExistEmailMatchBrokerDomainAndBrokerIsPublic() {
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
IdentityProviderRepresentation idpRep = organization.identityProviders().getIdentityProviders().get(0);
idpRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
idpRep.setHideOnLogin(false);
idpRep.getConfig().remove(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
testRealm().identityProviders().get(idpRep.getAlias()).update(idpRep);
@ -153,8 +153,8 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
idp = bc.setUpIdentityProvider();
idp.setAlias("second-idp");
idp.setInternalId(null);
idp.setHideOnLogin(false);
idp.getConfig().remove(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
idp.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
// create a second broker without a domain set
testRealm().identityProviders().create(idp).close();
getCleanup().addCleanup(testRealm().identityProviders().get("second-idp")::remove);
@ -167,7 +167,7 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
Assert.assertFalse(loginPage.isSocialButtonPresent(bc.getIDPAlias()));
Assert.assertTrue(loginPage.isSocialButtonPresent(idp.getAlias()));
idp.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.FALSE.toString());
idp.setHideOnLogin(true);
testRealm().identityProviders().get(idp.getAlias()).update(idp);
driver.navigate().refresh();
Assert.assertTrue(loginPage.isPasswordInputPresent());
@ -207,6 +207,7 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
IdentityProviderRepresentation idp = bc.setUpIdentityProvider();
idp.setAlias("realm-level-idp");
idp.setHideOnLogin(false);
Assert.assertFalse(loginPage.isSocialButtonPresent(idp.getAlias()));
testRealm().identityProviders().create(idp).close();
@ -224,7 +225,7 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
OrganizationIdentityProviderResource broker = organization.identityProviders().get(bc.getIDPAlias());
IdentityProviderRepresentation brokerRep = broker.toRepresentation();
brokerRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
brokerRep.setHideOnLogin(false);
brokerRep.getConfig().remove(IdentityProviderRedirectMode.EMAIL_MATCH.getKey());
testRealm().identityProviders().get(brokerRep.getAlias()).update(brokerRep);
@ -248,8 +249,8 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
OrganizationIdentityProviderResource broker = organization.identityProviders().get(bc.getIDPAlias());
IdentityProviderRepresentation brokerRep = broker.toRepresentation();
brokerRep.setHideOnLogin(false);
brokerRep.getConfig().remove(IdentityProviderRedirectMode.EMAIL_MATCH.getKey());
brokerRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
testRealm().identityProviders().get(brokerRep.getAlias()).update(brokerRep);
openIdentityFirstLoginPage(bc.getUserEmail(), true, brokerRep.getAlias(), false, true);
@ -302,13 +303,14 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
OrganizationIdentityProviderResource broker = organization.identityProviders().get(bc.getIDPAlias());
IdentityProviderRepresentation brokerRep = broker.toRepresentation();
brokerRep.setHideOnLogin(false);
brokerRep.getConfig().put(IdentityProviderRedirectMode.EMAIL_MATCH.getKey(), Boolean.FALSE.toString());
testRealm().identityProviders().get(brokerRep.getAlias()).update(brokerRep);
IdentityProviderRepresentation secondIdp = bc.setUpIdentityProvider();
secondIdp.setAlias("second-idp");
secondIdp.setInternalId(null);
secondIdp.setHideOnLogin(false);
secondIdp.getConfig().remove(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
secondIdp.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
testRealm().identityProviders().create(secondIdp).close();
getCleanup().addCleanup(testRealm().identityProviders().get("second-idp")::remove);
organization.identityProviders().addIdentityProvider(secondIdp.getAlias()).close();
@ -350,7 +352,7 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
public void testNoIDPRedirectWhenUserHasCredentialsSet() {
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
IdentityProviderRepresentation idpRep = organization.identityProviders().getIdentityProviders().get(0);
idpRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
idpRep.setHideOnLogin(false);
testRealm().identityProviders().get(idpRep.getAlias()).update(idpRep);
assertBrokerRegistration(organization, bc.getUserLogin(), bc.getUserEmail());
@ -467,9 +469,9 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
public void testDoNotRedirectToIdentityProviderAssociatedWithOrganizationDomain() {
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
IdentityProviderRepresentation idp = organization.identityProviders().get(bc.getIDPAlias()).toRepresentation();
idp.setHideOnLogin(false);
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);
openIdentityFirstLoginPage(bc.getUserEmail(), false, idp.getAlias(), false, false);
@ -484,15 +486,15 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
String org0Name = "org-0";
OrganizationResource org0 = testRealm().organizations().get(createOrganization(org0Name).getId());
IdentityProviderRepresentation org0Broker = org0.identityProviders().getIdentityProviders().get(0);
org0Broker.setHideOnLogin(false);
org0Broker.getConfig().remove(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
org0Broker.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
testRealm().identityProviders().get(org0Broker.getAlias()).update(org0Broker);
String org1Name = "org-1";
OrganizationResource org1 = testRealm().organizations().get(createOrganization(org1Name).getId());
IdentityProviderRepresentation org1Broker = org1.identityProviders().getIdentityProviders().get(0);
org1Broker.setHideOnLogin(false);
org1Broker.getConfig().remove(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
org1Broker.getConfig().remove(IdentityProviderRedirectMode.EMAIL_MATCH.getKey());
org1Broker.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
testRealm().identityProviders().get(org1Broker.getAlias()).update(org1Broker);
oauth.clientId("broker-app");
@ -520,8 +522,8 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
idp = bc.setUpIdentityProvider();
idp.setAlias("second-idp");
idp.setInternalId(null);
idp.setHideOnLogin(false);
idp.getConfig().remove(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
idp.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
// create a second broker without a domain set
testRealm().identityProviders().create(idp).close();
getCleanup().addCleanup(testRealm().identityProviders().get("second-idp")::remove);
@ -556,8 +558,8 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
idp = bc.setUpIdentityProvider();
idp.setAlias("second-idp");
idp.setInternalId(null);
idp.setHideOnLogin(false);
idp.getConfig().put(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE, "other.org");
idp.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
// create a second broker without a domain set
testRealm().identityProviders().create(idp).close();
getCleanup().addCleanup(testRealm().identityProviders().get("second-idp")::remove);
@ -590,7 +592,7 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
idp = bc.setUpIdentityProvider();
idp.setAlias("second-idp");
idp.setInternalId(null);
idp.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
idp.setHideOnLogin(false);
// create a second broker without a domain set
testRealm().identityProviders().create(idp).close();
getCleanup().addCleanup(testRealm().identityProviders().get("second-idp")::remove);
@ -609,6 +611,7 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
IdentityProviderRepresentation idp = bc.setUpIdentityProvider();
idp.setAlias("realm-idp");
idp.setInternalId(null);
idp.setHideOnLogin(false);
// create a second broker without a domain set
testRealm().identityProviders().create(idp).close();
@ -628,7 +631,7 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
IdentityProviderRepresentation idpRep = organization.identityProviders().getIdentityProviders().get(0);
// make sure the user can select this idp from the organization when authenticating
idpRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
idpRep.setHideOnLogin(false);
idpRep.getConfig().remove(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
testRealm().identityProviders().get(idpRep.getAlias()).update(idpRep);
@ -653,7 +656,7 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
IdentityProviderRepresentation idpRep = organization.identityProviders().getIdentityProviders().get(0);
// make sure the user can select this idp from the organization when authenticating
idpRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
idpRep.setHideOnLogin(false);
idpRep.getConfig().remove(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
testRealm().identityProviders().get(idpRep.getAlias()).update(idpRep);

View file

@ -81,6 +81,8 @@ public class BrokerConfigurationWrapper implements BrokerConfiguration {
public IdentityProviderRepresentation setUpIdentityProvider() {
IdentityProviderRepresentation broker = delegate.setUpIdentityProvider();
broker.setAlias(getIDPAlias());
// by default set the test org idps as not available for login pages.
broker.setHideOnLogin(true);
return broker;
}

View file

@ -23,7 +23,6 @@ import static org.junit.Assert.assertFalse;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertNotNull;
import static org.keycloak.models.OrganizationModel.BROKER_PUBLIC;
import static org.keycloak.models.OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE;
import jakarta.ws.rs.BadRequestException;
@ -111,7 +110,6 @@ public class OrganizationIdentityProviderTest extends AbstractOrganizationTest {
//remove Org related stuff from the template
idpTemplate.setOrganizationId(null);
idpTemplate.getConfig().remove(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
idpTemplate.getConfig().remove(OrganizationModel.BROKER_PUBLIC);
idpTemplate.getConfig().remove(OrganizationModel.IdentityProviderRedirectMode.EMAIL_MATCH.getKey());
for (int i = 0; i < 5; i++) {
@ -191,7 +189,6 @@ public class OrganizationIdentityProviderTest extends AbstractOrganizationTest {
// broker no longer linked to the org
Assert.assertNull(idpRep.getOrganizationId());
Assert.assertNull(idpRep.getConfig().get(ORGANIZATION_DOMAIN_ATTRIBUTE));
Assert.assertNull(idpRep.getConfig().get(BROKER_PUBLIC));
}
@Test

View file

@ -290,8 +290,8 @@ public class OrganizationCacheTest extends AbstractOrganizationTest {
IdentityProviderRepresentation idpRep = testRealm().identityProviders().get("orga-identity-provider").toRepresentation();
idpRep.setInternalId(null);
idpRep.setOrganizationId(null);
idpRep.setHideOnLogin(false);
idpRep.getConfig().remove(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
idpRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
for (int i = 0; i < 10; i++) {
final String alias = "org-idp-" + i;
@ -372,8 +372,6 @@ public class OrganizationCacheTest extends AbstractOrganizationTest {
idpRep.setEnabled((i % 2) == 0); // half of the IDPs will be disabled and won't qualify for login.
idpRep.setDisplayName("Broker " + i);
idpRep.setProviderId("keycloak-oidc");
if (i >= 10)
idpRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
testRealm().identityProviders().create(idpRep).close();
getCleanup().addCleanup(testRealm().identityProviders().get("alias")::remove);
}
@ -497,7 +495,6 @@ public class OrganizationCacheTest extends AbstractOrganizationTest {
// 4- finally, change one of the realm-level login IDPs, linking it to an org - although it still qualifies for login, it is now
// linked to an org, which should invalidate all login caches.
idpRep = testRealm().identityProviders().get("idp-alias-20").toRepresentation();
idpRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
testRealm().identityProviders().get("idp-alias-20").update(idpRep);
testRealm().organizations().get(orgaId).identityProviders().addIdentityProvider("idp-alias-20");

View file

@ -46,7 +46,6 @@ import org.keycloak.admin.client.resource.ClientScopeResource;
import org.keycloak.admin.client.resource.OrganizationResource;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.UriUtils;
import org.keycloak.models.OrganizationModel;
import org.keycloak.organization.protocol.mappers.oidc.OrganizationMembershipMapper;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
@ -125,9 +124,9 @@ public class OrganizationOIDCProtocolMapperTest extends AbstractOrganizationTest
@SuppressWarnings("unchecked")
@Test
public void testOrganizationScopeMapsSpecificOrganization() {
OrganizationRepresentation orgA = createOrganization("orga", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgA = createOrganization("orga", true);
MemberRepresentation member = addMember(testRealm().organizations().get(orgA.getId()), "member@" + orgA.getDomains().iterator().next().getName());
OrganizationRepresentation orgB = createOrganization("orgb", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgB = createOrganization("orgb", true);
testRealm().organizations().get(orgB.getId()).members().addMember(member.getId()).close();
// resolve organization based on the organization scope value
@ -156,9 +155,9 @@ public class OrganizationOIDCProtocolMapperTest extends AbstractOrganizationTest
@Test
public void testOrganizationScopeMapsAllOrganizations() {
OrganizationRepresentation orgA = createOrganization("orga", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgA = createOrganization("orga", true);
MemberRepresentation member = addMember(testRealm().organizations().get(orgA.getId()), "member@" + orgA.getDomains().iterator().next().getName());
OrganizationRepresentation orgB = createOrganization("orgb", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgB = createOrganization("orgb", true);
testRealm().organizations().get(orgB.getId()).members().addMember(member.getId()).close();
// resolve organization based on the organization scope value
@ -204,7 +203,7 @@ public class OrganizationOIDCProtocolMapperTest extends AbstractOrganizationTest
@Test
public void testOrganizationScopeAnyMapsSingleOrganization() {
OrganizationRepresentation orgA = createOrganization("orga", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgA = createOrganization("orga", true);
MemberRepresentation member = addMember(testRealm().organizations().get(orgA.getId()), "member@" + orgA.getDomains().iterator().next().getName());
// resolve organization based on the organization scope value
@ -220,9 +219,9 @@ public class OrganizationOIDCProtocolMapperTest extends AbstractOrganizationTest
@Test
public void testOrganizationScopeAnyAskUserToSelectOrganization() {
OrganizationRepresentation orgA = createOrganization("orga", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgA = createOrganization("orga", true);
MemberRepresentation member = addMember(testRealm().organizations().get(orgA.getId()), "member@" + orgA.getDomains().iterator().next().getName());
OrganizationRepresentation orgB = createOrganization("orgb", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgB = createOrganization("orgb", true);
testRealm().organizations().get(orgB.getId()).members().addMember(member.getId()).close();
oauth.clientId("broker-app");
oauth.scope("organization");
@ -248,9 +247,9 @@ public class OrganizationOIDCProtocolMapperTest extends AbstractOrganizationTest
@Test
public void testRefreshTokenWithAllOrganizationsAskingForSpecificOrganization() {
OrganizationRepresentation orgA = createOrganization("orga", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgA = createOrganization("orga", true);
MemberRepresentation member = addMember(testRealm().organizations().get(orgA.getId()), "member@" + orgA.getDomains().iterator().next().getName());
OrganizationRepresentation orgB = createOrganization("orgb", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgB = createOrganization("orgb", true);
testRealm().organizations().get(orgB.getId()).members().addMember(member.getId()).close();
// identity-first login will respect the organization provided in the scope even though the user email maps to a different organization
oauth.clientId("broker-app");
@ -281,9 +280,9 @@ public class OrganizationOIDCProtocolMapperTest extends AbstractOrganizationTest
@Test
public void testRefreshTokenWithSingleOrganizationsAskingAllOrganizations() {
OrganizationRepresentation orgA = createOrganization("orga", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgA = createOrganization("orga", true);
MemberRepresentation member = addMember(testRealm().organizations().get(orgA.getId()), "member@" + orgA.getDomains().iterator().next().getName());
OrganizationRepresentation orgB = createOrganization("orgb", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgB = createOrganization("orgb", true);
testRealm().organizations().get(orgB.getId()).members().addMember(member.getId()).close();
// identity-first login will respect the organization provided in the scope even though the user email maps to a different organization
oauth.clientId("broker-app");
@ -316,9 +315,9 @@ public class OrganizationOIDCProtocolMapperTest extends AbstractOrganizationTest
@Test
public void testRefreshTokenWithSingleOrganizationsAskingDifferentOrganization() {
OrganizationRepresentation orgA = createOrganization("orga", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgA = createOrganization("orga", true);
MemberRepresentation member = addMember(testRealm().organizations().get(orgA.getId()), "member@" + orgA.getDomains().iterator().next().getName());
OrganizationRepresentation orgB = createOrganization("orgb", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgB = createOrganization("orgb", true);
testRealm().organizations().get(orgB.getId()).members().addMember(member.getId()).close();
// identity-first login will respect the organization provided in the scope even though the user email maps to a different organization
oauth.clientId("broker-app");
@ -349,9 +348,9 @@ public class OrganizationOIDCProtocolMapperTest extends AbstractOrganizationTest
@Test
public void testRefreshTokenScopeAnyAskingAllOrganizations() {
OrganizationRepresentation orgA = createOrganization("orga", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgA = createOrganization("orga", true);
MemberRepresentation member = addMember(testRealm().organizations().get(orgA.getId()), "member@" + orgA.getDomains().iterator().next().getName());
OrganizationRepresentation orgB = createOrganization("orgb", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgB = createOrganization("orgb", true);
testRealm().organizations().get(orgB.getId()).members().addMember(member.getId()).close();
oauth.clientId("broker-app");
String originalScope = "organization";
@ -385,9 +384,9 @@ public class OrganizationOIDCProtocolMapperTest extends AbstractOrganizationTest
@Test
public void testRefreshTokenScopeAnyAskingSingleOrganization() {
OrganizationRepresentation orgA = createOrganization("orga", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgA = createOrganization("orga", true);
MemberRepresentation member = addMember(testRealm().organizations().get(orgA.getId()), "member@" + orgA.getDomains().iterator().next().getName());
OrganizationRepresentation orgB = createOrganization("orgb", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgB = createOrganization("orgb", true);
testRealm().organizations().get(orgB.getId()).members().addMember(member.getId()).close();
oauth.clientId("broker-app");
String originalScope = "organization";
@ -455,9 +454,9 @@ public class OrganizationOIDCProtocolMapperTest extends AbstractOrganizationTest
@Test
public void testOrganizationsClaimAsList() throws Exception {
OrganizationRepresentation orgA = createOrganization("orga", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgA = createOrganization("orga", true);
MemberRepresentation member = addMember(testRealm().organizations().get(orgA.getId()), "member@" + orgA.getDomains().iterator().next().getName());
OrganizationRepresentation orgB = createOrganization("orgb", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgB = createOrganization("orgb", true);
testRealm().organizations().get(orgB.getId()).members().addMember(member.getId()).close();
setMapperConfig(OIDCAttributeMapperHelper.JSON_TYPE, "String");
@ -473,9 +472,9 @@ public class OrganizationOIDCProtocolMapperTest extends AbstractOrganizationTest
@Test
public void testOrganizationsClaimSingleValued() throws Exception {
OrganizationRepresentation orgA = createOrganization("orga", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgA = createOrganization("orga", true);
MemberRepresentation member = addMember(testRealm().organizations().get(orgA.getId()), "member@" + orgA.getDomains().iterator().next().getName());
OrganizationRepresentation orgB = createOrganization("orgb", Map.of(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()));
OrganizationRepresentation orgB = createOrganization("orgb", true);
testRealm().organizations().get(orgB.getId()).members().addMember(member.getId()).close();
setMapperConfig(ProtocolMapperUtils.MULTIVALUED, Boolean.FALSE.toString());

View file

@ -430,6 +430,7 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
// create non-org idp in a realm
String idpAlias = "former-non-org-identity-provider";
IdentityProviderRepresentation idpRep = brokerConfigFunction.apply("former-non-org").setUpIdentityProvider();
idpRep.setHideOnLogin(false);
try (Response response = testRealm().identityProviders().create(idpRep)) {
assertThat(response.getStatus(), equalTo(Status.CREATED.getStatusCode()));
getCleanup().addCleanup(testRealm().identityProviders().get(bc.getIDPAlias())::remove);