Make sure disabled organizations are not available from selection

Closes #45874

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
Pedro Igor 2026-01-30 17:17:35 -03:00 committed by GitHub
parent c652adff78
commit 2dab08d5ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 115 additions and 15 deletions

View file

@ -199,27 +199,27 @@ public class OrganizationAuthenticator extends IdentityProviderAuthenticator {
MultivaluedMap<String, String> parameters = request.getDecodedFormParameters();
// parameter from the organization selection page
List<String> alias = parameters.getOrDefault(OrganizationModel.ORGANIZATION_ATTRIBUTE, List.of());
OrganizationModel organization;
if (alias.isEmpty()) {
OrganizationModel organization = Organizations.resolveOrganization(session, user, domain);
if (isSSOAuthentication(authSession) && organization != null) {
organization = Organizations.resolveOrganization(session, user, domain);
if (organization != null && isSSOAuthentication(authSession)) {
// make sure the organization selected by the user is available from the client session when running mappers and issuing tokens
authSession.setClientNote(OrganizationModel.ORGANIZATION_ATTRIBUTE, organization.getId());
}
return organization;
} else {
OrganizationProvider provider = getOrganizationProvider();
organization = provider.getByAlias(alias.get(0));
}
OrganizationProvider provider = getOrganizationProvider();
OrganizationModel organization = provider.getByAlias(alias.get(0));
if (organization == null) {
if (organization == null || !organization.isEnabled()) {
return null;
}
// make sure the organization selected by the user is available from the client session when running mappers and issuing tokens
authSession.setClientNote(OrganizationModel.ORGANIZATION_ATTRIBUTE, organization.getId());
if (!alias.isEmpty() || isSSOAuthentication(authSession)) {
// make sure the organization selected by the user is available from the client session when running mappers and issuing tokens
authSession.setClientNote(OrganizationModel.ORGANIZATION_ATTRIBUTE, organization.getId());
}
return organization;
}
@ -237,7 +237,7 @@ public class OrganizationAuthenticator extends IdentityProviderAuthenticator {
}
OrganizationProvider provider = getOrganizationProvider();
Stream<OrganizationModel> organizations = provider.getByMember(user);
Stream<OrganizationModel> organizations = provider.getByMember(user).filter(OrganizationModel::isEnabled);
if (organizations.count() > 1) {
LoginFormsProvider form = context.form();

View file

@ -116,7 +116,7 @@ public enum OrganizationScope {
return Stream.empty();
}
List<OrganizationModel> organizations = getProvider(session).getByMember(user).toList();
List<OrganizationModel> organizations = getProvider(session).getByMember(user).filter(OrganizationModel::isEnabled).toList();
if (organizations.size() == 1) {
return organizations.stream();

View file

@ -272,14 +272,114 @@ public class OrganizationOIDCProtocolMapperTest extends AbstractOrganizationTest
selectOrganizationPage.selectOrganization(orgB.getAlias());
loginPage.login(memberPassword);
AccessTokenResponse response = assertSuccessfulCodeGrant();
// for now, return the organization scope in the response and access token even though no organization is mapped into the token
// once we support the user to select an organization, the selected organization will be mapped
assertThat(response.getScope(), containsString("organization"));
AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
List<String> organizations = (List<String>) accessToken.getOtherClaims().get(OAuth2Constants.ORGANIZATION);
assertThat(accessToken.getOtherClaims().keySet(), hasItem(OAuth2Constants.ORGANIZATION));
assertThat(organizations.contains(orgA.getAlias()), is(false));
assertThat(organizations.contains(orgB.getAlias()), is(true));
testRealm().users().get(member.getId()).logout();
loginPage.open(bc.consumerRealmName());
loginPage.loginUsername(member.getEmail());
selectOrganizationPage.assertCurrent();
assertTrue(selectOrganizationPage.isOrganizationButtonPresent(orgA.getAlias()));
assertTrue(selectOrganizationPage.isOrganizationButtonPresent(orgB.getAlias()));
orgB.setEnabled(false);
testRealm().organizations().get(orgB.getId()).update(orgB).close();
loginPage.open(bc.consumerRealmName());
loginPage.loginUsername(member.getEmail());
assertFalse(selectOrganizationPage.isCurrent());
loginPage.login(memberPassword);
response = assertSuccessfulCodeGrant();
assertThat(response.getScope(), containsString("organization"));
accessToken = oauth.verifyToken(response.getAccessToken());
organizations = (List<String>) accessToken.getOtherClaims().get(OAuth2Constants.ORGANIZATION);
assertThat(accessToken.getOtherClaims().keySet(), hasItem(OAuth2Constants.ORGANIZATION));
assertThat(organizations.contains(orgA.getAlias()), is(true));
assertThat(organizations.contains(orgB.getAlias()), is(false));
}
@Test
public void testOrganizationScopeSelectDisabledOrganization() {
OrganizationRepresentation orgA = createOrganization("orga", true);
MemberRepresentation member = addMember(testRealm().organizations().get(orgA.getId()), "member@" + orgA.getDomains().iterator().next().getName());
OrganizationRepresentation orgB = createOrganization("orgb", true);
testRealm().organizations().get(orgB.getId()).members().addMember(member.getId()).close();
oauth.client("broker-app", KcOidcBrokerConfiguration.CONSUMER_BROKER_APP_SECRET);
oauth.scope("organization");
loginPage.open(bc.consumerRealmName());
loginPage.loginUsername(member.getEmail());
assertTrue(selectOrganizationPage.isCurrent());
assertFalse(driver.getPageSource().contains("kc-select-try-another-way-form"));
assertTrue(selectOrganizationPage.isOrganizationButtonPresent(orgA.getAlias()));
assertTrue(selectOrganizationPage.isOrganizationButtonPresent(orgB.getAlias()));
selectOrganizationPage.selectOrganization(orgB.getAlias());
loginPage.login(memberPassword);
AccessTokenResponse response = assertSuccessfulCodeGrant();
assertThat(response.getScope(), containsString("organization"));
AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
List<String> organizations = (List<String>) accessToken.getOtherClaims().get(OAuth2Constants.ORGANIZATION);
assertThat(accessToken.getOtherClaims().keySet(), hasItem(OAuth2Constants.ORGANIZATION));
assertThat(organizations.contains(orgA.getAlias()), is(false));
assertThat(organizations.contains(orgB.getAlias()), is(true));
testRealm().users().get(member.getId()).logout();
loginPage.open(bc.consumerRealmName());
loginPage.loginUsername(member.getEmail());
selectOrganizationPage.assertCurrent();
assertTrue(selectOrganizationPage.isOrganizationButtonPresent(orgA.getAlias()));
assertTrue(selectOrganizationPage.isOrganizationButtonPresent(orgB.getAlias()));
orgB.setEnabled(false);
testRealm().organizations().get(orgB.getId()).update(orgB).close();
loginPage.open(bc.consumerRealmName());
loginPage.loginUsername(member.getEmail());
assertFalse(selectOrganizationPage.isCurrent());
loginPage.login(memberPassword);
response = assertSuccessfulCodeGrant();
assertThat(response.getScope(), containsString("organization"));
accessToken = oauth.verifyToken(response.getAccessToken());
organizations = (List<String>) accessToken.getOtherClaims().get(OAuth2Constants.ORGANIZATION);
assertThat(accessToken.getOtherClaims().keySet(), hasItem(OAuth2Constants.ORGANIZATION));
assertThat(organizations.contains(orgA.getAlias()), is(true));
assertThat(organizations.contains(orgB.getAlias()), is(false));
}
@Test
public void testOrganizationScopeSpecifyDisabledOrganization() {
OrganizationRepresentation orgA = createOrganization("orga", true);
MemberRepresentation member = addMember(testRealm().organizations().get(orgA.getId()), "member@" + orgA.getDomains().iterator().next().getName());
OrganizationRepresentation orgB = createOrganization("orgb", true);
testRealm().organizations().get(orgB.getId()).members().addMember(member.getId()).close();
oauth.client("broker-app", KcOidcBrokerConfiguration.CONSUMER_BROKER_APP_SECRET);
oauth.scope("organization");
loginPage.open(bc.consumerRealmName());
loginPage.loginUsername(member.getEmail());
assertTrue(selectOrganizationPage.isCurrent());
assertFalse(driver.getPageSource().contains("kc-select-try-another-way-form"));
assertTrue(selectOrganizationPage.isOrganizationButtonPresent(orgA.getAlias()));
assertTrue(selectOrganizationPage.isOrganizationButtonPresent(orgB.getAlias()));
selectOrganizationPage.selectOrganization(orgB.getAlias());
loginPage.login(memberPassword);
AccessTokenResponse response = assertSuccessfulCodeGrant();
assertThat(response.getScope(), containsString("organization"));
AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
List<String> organizations = (List<String>) accessToken.getOtherClaims().get(OAuth2Constants.ORGANIZATION);
assertThat(accessToken.getOtherClaims().keySet(), hasItem(OAuth2Constants.ORGANIZATION));
assertThat(organizations.contains(orgA.getAlias()), is(false));
assertThat(organizations.contains(orgB.getAlias()), is(true));
testRealm().users().get(member.getId()).logout();
loginPage.open(bc.consumerRealmName());
loginPage.loginUsername(member.getEmail());
selectOrganizationPage.assertCurrent();
assertTrue(selectOrganizationPage.isOrganizationButtonPresent(orgA.getAlias()));
assertTrue(selectOrganizationPage.isOrganizationButtonPresent(orgB.getAlias()));
orgB.setEnabled(false);
testRealm().organizations().get(orgB.getId()).update(orgB).close();
oauth.scope("organization:" + orgB.getAlias());
oauth.openLoginForm();
assertTrue(driver.getCurrentUrl().contains("Invalid+scopes%3A+openid+organization"));
}
@Test