Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@

package org.keycloak.tests.organization.admin;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.core.Response;
Expand All @@ -41,17 +43,24 @@
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testframework.annotations.InjectRealm;
import org.keycloak.testframework.oauth.OAuthClient;
import org.keycloak.testframework.realm.ClientBuilder;
import org.keycloak.testframework.realm.CredentialBuilder;
import org.keycloak.testframework.realm.IdentityProviderBuilder;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.realm.RealmBuilder;
import org.keycloak.testframework.realm.RealmConfig;
import org.keycloak.testframework.realm.UserBuilder;
import org.keycloak.testframework.realm.UserConfig;
import org.keycloak.testframework.ui.page.LoginPage;
import org.keycloak.testframework.ui.page.LoginUpdateProfilePage;
import org.keycloak.testframework.ui.page.LoginUsernamePage;
import org.keycloak.testframework.util.ApiUtil;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Base class for organization tests in the new test framework.
Expand Down Expand Up @@ -235,6 +244,88 @@ protected void setMapperConfig(String key, String value) {
orgScopeResource.getProtocolMappers().update(orgMapper.getId(), orgMapper);
}

/**
* Builds an IdP representation pointing to a real provider realm's OIDC endpoints.
*/
public static IdentityProviderRepresentation createRealOrgBroker(String alias, ManagedRealm providerRealm) {
IdentityProviderRepresentation idp = new IdentityProviderRepresentation();
idp.setAlias(alias);
idp.setProviderId("keycloak-oidc");
idp.setEnabled(true);
idp.setTrustEmail(true);
String providerBaseUrl = providerRealm.getBaseUrl();
idp.setConfig(new HashMap<>(Map.of(
"clientId", CLIENT_ID,
"clientSecret", CLIENT_SECRET,
"authorizationUrl", providerBaseUrl + "/protocol/openid-connect/auth",
"tokenUrl", providerBaseUrl + "/protocol/openid-connect/token",
"userInfoUrl", providerBaseUrl + "/protocol/openid-connect/userinfo",
"defaultScope", "email profile",
"syncMode", "IMPORT"
)));
return idp;
}

/**
* Performs identity-first login through a broker: enters email on the consumer realm login page,
* follows the redirect to the provider realm, fills credentials, and optionally handles first-time
* login profile update. Registers cleanup for the federated user created in the consumer realm.
*/
protected void loginViaBroker(String email, String username, String password,
OAuthClient oauth, LoginUsernamePage loginUsernamePage, LoginPage loginPage,
ManagedRealm providerRealm) {
loginViaBroker(email, username, password, null, oauth, loginUsernamePage, loginPage, null, providerRealm);
}

protected void loginViaBroker(String email, String username, String password,
String updateEmail, OAuthClient oauth, LoginUsernamePage loginUsernamePage,
LoginPage loginPage, LoginUpdateProfilePage loginUpdateProfilePage,
ManagedRealm providerRealm) {
oauth.openLoginForm();
loginUsernamePage.fillLoginWithUsernameOnly(email);
loginUsernamePage.submit();

assertTrue(Objects.requireNonNull(oauth.getDriver().getCurrentUrl()).contains("/realms/" + providerRealm.getName() + "/"),
"Should be on provider realm login page");

loginPage.fillLogin(username, password);
loginPage.submit();

if (updateEmail != null) {
loginUpdateProfilePage.update("Firstname", "Lastname", updateEmail);
}

List<UserRepresentation> users = realm.admin().users().search(username);
assertEquals(1, users.size(), "Federated user should be created in consumer realm");

String userId = users.get(0).getId();
realm.cleanup().add(r -> {
try {
r.users().get(userId).remove();
} catch (NotFoundException ignored) {}
});
}

/**
* Performs broker registration and verifies the user becomes an organization member.
* Equivalent to the old AbstractOrganizationTest.assertBrokerRegistration().
*/
protected void assertBrokerRegistration(OrganizationResource organization,
String username, String email,
OAuthClient oauth, LoginUsernamePage loginUsernamePage, LoginPage loginPage,
LoginUpdateProfilePage loginUpdateProfilePage, ManagedRealm providerRealm) {
loginViaBroker(email, username, "password", email,
oauth, loginUsernamePage, loginPage, loginUpdateProfilePage, providerRealm);

assertIsMember(email, organization);
}

protected void assertIsMember(String userEmail, OrganizationResource organization) {
UserRepresentation account = getUserRepresentation(userEmail);
UserRepresentation member = organization.members().member(account.getId()).toRepresentation();
assertEquals(account.getId(), member.getId());
}

/**
* Creates an OIDC identity provider representation for the given organization name.
*/
Expand Down Expand Up @@ -304,4 +395,15 @@ public RealmBuilder configure(RealmBuilder realm) {
);
}
}

public static class AliceUserConf implements UserConfig {
@Override
public UserBuilder configure(UserBuilder builder) {
return builder.username("alice")
.password("password")
.email("alice@neworg.org")
.emailVerified(true)
.name("Alice", "Org");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

package org.keycloak.testsuite.organization.mapper;
package org.keycloak.tests.organization.mapper;

import java.util.HashMap;
import java.util.List;
Expand All @@ -41,12 +41,15 @@
import org.keycloak.representations.idm.OrganizationRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.organization.admin.AbstractOrganizationTest;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.oauth.OAuthClient;
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
import org.keycloak.testframework.util.ApiUtil;
import org.keycloak.tests.organization.admin.AbstractOrganizationTest;
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;

import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
Expand All @@ -61,9 +64,13 @@
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.startsWith;

@KeycloakIntegrationTest
public class OrganizationGroupMembershipOIDCMapperTest extends AbstractOrganizationTest {

@Before
@InjectOAuthClient
OAuthClient oauth;

@BeforeEach
public void addGroupMapper() {
// Reset to defaults
setMapperConfig(ProtocolMapperUtils.MULTIVALUED, null);
Expand All @@ -73,7 +80,7 @@ public void addGroupMapper() {
setMapperConfig(OrganizationMembershipMapper.ADD_ORGANIZATION_ID, null);

// Add the organization group membership mapper to the organization scope
ClientScopeRepresentation orgScope = managedRealm.admin().clientScopes().findAll().stream()
ClientScopeRepresentation orgScope = realm.admin().clientScopes().findAll().stream()
.filter(s -> OIDCLoginProtocolFactory.ORGANIZATION.equals(s.getName()))
.findAny()
.orElseThrow();
Expand All @@ -87,7 +94,7 @@ public void addGroupMapper() {
return;
}

ClientScopeResource orgScopeResource = managedRealm.admin().clientScopes().get(orgScope.getId());
ClientScopeResource orgScopeResource = realm.admin().clientScopes().get(orgScope.getId());

ProtocolMapperRepresentation groupMapper = new ProtocolMapperRepresentation();
groupMapper.setName("organization-groups");
Expand All @@ -105,11 +112,11 @@ public void addGroupMapper() {
}

private void setGroupMapperConfig(String key, String value) {
ClientScopeRepresentation orgScope = managedRealm.admin().clientScopes().findAll().stream()
ClientScopeRepresentation orgScope = realm.admin().clientScopes().findAll().stream()
.filter(s -> OIDCLoginProtocolFactory.ORGANIZATION.equals(s.getName()))
.findAny()
.orElseThrow();
ClientScopeResource orgScopeResource = managedRealm.admin().clientScopes().get(orgScope.getId());
ClientScopeResource orgScopeResource = realm.admin().clientScopes().get(orgScope.getId());

ProtocolMapperRepresentation groupMapper = orgScopeResource.getProtocolMappers().getMappers().stream()
.filter(m -> "organization-groups".equals(m.getName()))
Expand All @@ -131,7 +138,7 @@ private void setGroupMapperConfig(String key, String value) {
public void testNestedGroupsWithRelativePaths() throws Exception {
// Create organization
OrganizationRepresentation orgRep = createOrganization("acme");
OrganizationResource org = managedRealm.admin().organizations().get(orgRep.getId());
OrganizationResource org = realm.admin().organizations().get(orgRep.getId());

// Add member
MemberRepresentation member = addMember(org);
Expand Down Expand Up @@ -184,7 +191,7 @@ public void testNestedGroupsWithRelativePaths() throws Exception {
public void testEmptyGroupsWhenUserHasNoGroups() throws Exception {
// Create organization
OrganizationRepresentation orgRep = createOrganization("acme");
OrganizationResource org = managedRealm.admin().organizations().get(orgRep.getId());
OrganizationResource org = realm.admin().organizations().get(orgRep.getId());

// Add member (but don't add to any groups)
addMember(org);
Expand All @@ -211,8 +218,8 @@ public void testCompositionWithAttributesAndId() throws Exception {
OrganizationRepresentation orgA = createOrganization("org-a");
OrganizationRepresentation orgB = createOrganization("org-b");

OrganizationResource orgAResource = managedRealm.admin().organizations().get(orgA.getId());
OrganizationResource orgBResource = managedRealm.admin().organizations().get(orgB.getId());
OrganizationResource orgAResource = realm.admin().organizations().get(orgA.getId());
OrganizationResource orgBResource = realm.admin().organizations().get(orgB.getId());

// Add member to both orgs
MemberRepresentation member = addMember(orgAResource);
Expand Down Expand Up @@ -275,9 +282,9 @@ public void testCompositionWithStringTypeOrgMapper() throws Exception {
OrganizationRepresentation orgB = createOrganization("org-b");
OrganizationRepresentation orgC = createOrganization("org-c");

OrganizationResource orgAResource = managedRealm.admin().organizations().get(orgA.getId());
OrganizationResource orgBResource = managedRealm.admin().organizations().get(orgB.getId());
OrganizationResource orgCResource = managedRealm.admin().organizations().get(orgC.getId());
OrganizationResource orgAResource = realm.admin().organizations().get(orgA.getId());
OrganizationResource orgBResource = realm.admin().organizations().get(orgB.getId());
OrganizationResource orgCResource = realm.admin().organizations().get(orgC.getId());

// Add member to all THREE orgs
MemberRepresentation member = addMember(orgAResource);
Expand Down Expand Up @@ -332,7 +339,7 @@ public void testCompositionWithStringTypeOrgMapper() throws Exception {
@SuppressWarnings("unchecked")
public void testCustomClaimName() throws Exception {
OrganizationRepresentation orgRep = createOrganization("acme");
OrganizationResource org = managedRealm.admin().organizations().get(orgRep.getId());
OrganizationResource org = realm.admin().organizations().get(orgRep.getId());
MemberRepresentation member = addMember(org);

GroupRepresentation engineering = new GroupRepresentation();
Expand Down Expand Up @@ -366,7 +373,7 @@ public void testCustomClaimName() throws Exception {
@SuppressWarnings("unchecked")
public void testDottedClaimName() throws Exception {
OrganizationRepresentation orgRep = createOrganization("acme");
OrganizationResource org = managedRealm.admin().organizations().get(orgRep.getId());
OrganizationResource org = realm.admin().organizations().get(orgRep.getId());
MemberRepresentation member = addMember(org);

GroupRepresentation engineering = new GroupRepresentation();
Expand Down Expand Up @@ -404,7 +411,7 @@ public void testDottedClaimName() throws Exception {
@SuppressWarnings("unchecked")
public void testNoNpeWhenOrganizationScopeNotRequested() throws Exception {
OrganizationRepresentation orgRep = createOrganization("acme");
OrganizationResource org = managedRealm.admin().organizations().get(orgRep.getId());
OrganizationResource org = realm.admin().organizations().get(orgRep.getId());
addMember(org);

// Authenticate WITHOUT organization scope - should not throw NPE
Expand All @@ -428,7 +435,7 @@ public void testGroupRoleMappingsInToken() throws Exception {

// Create organization
OrganizationRepresentation orgRep = createOrganization("acme");
OrganizationResource org = managedRealm.admin().organizations().get(orgRep.getId());
OrganizationResource org = realm.admin().organizations().get(orgRep.getId());

// Add member
MemberRepresentation member = addMember(org);
Expand All @@ -443,21 +450,21 @@ public void testGroupRoleMappingsInToken() throws Exception {

// Create a realm role and assign to the group
RoleRepresentation realmRole = new RoleRepresentation("org-token-test-role", "", false);
managedRealm.admin().roles().create(realmRole);
RoleRepresentation createdRealmRole = managedRealm.admin().roles().get("org-token-test-role").toRepresentation();
realm.admin().roles().create(realmRole);
RoleRepresentation createdRealmRole = realm.admin().roles().get("org-token-test-role").toRepresentation();
org.groups().group(engineeringId).roles().realmLevel().add(List.of(createdRealmRole));

// Create a client with a role and assign to the group
ClientRepresentation clientRep = new ClientRepresentation();
clientRep.setClientId("org-token-test-client");
clientRep.setEnabled(true);
String clientUuid;
try (Response response = managedRealm.admin().clients().create(clientRep)) {
try (Response response = realm.admin().clients().create(clientRep)) {
clientUuid = ApiUtil.getCreatedId(response);
}
RoleRepresentation clientRole = new RoleRepresentation("org-token-client-role", "", false);
managedRealm.admin().clients().get(clientUuid).roles().create(clientRole);
RoleRepresentation createdClientRole = managedRealm.admin().clients().get(clientUuid).roles().get("org-token-client-role").toRepresentation();
realm.admin().clients().get(clientUuid).roles().create(clientRole);
RoleRepresentation createdClientRole = realm.admin().clients().get(clientUuid).roles().get("org-token-client-role").toRepresentation();
org.groups().group(engineeringId).roles().clientLevel(clientUuid).add(List.of(createdClientRole));

// Add member to group
Expand Down Expand Up @@ -499,7 +506,7 @@ public void testGroupRoleMappingsNotIncludedByDefault() throws Exception {
// Don't enable addGroupRoleMappings — default is off

OrganizationRepresentation orgRep = createOrganization("acme");
OrganizationResource org = managedRealm.admin().organizations().get(orgRep.getId());
OrganizationResource org = realm.admin().organizations().get(orgRep.getId());
MemberRepresentation member = addMember(org);

GroupRepresentation engineering = new GroupRepresentation();
Expand All @@ -511,8 +518,8 @@ public void testGroupRoleMappingsNotIncludedByDefault() throws Exception {

// Create and assign a realm role
RoleRepresentation realmRole = new RoleRepresentation("default-off-role", "", false);
managedRealm.admin().roles().create(realmRole);
RoleRepresentation createdRole = managedRealm.admin().roles().get("default-off-role").toRepresentation();
realm.admin().roles().create(realmRole);
RoleRepresentation createdRole = realm.admin().roles().get("default-off-role").toRepresentation();
org.groups().group(engineeringId).roles().realmLevel().add(List.of(createdRole));

org.groups().group(engineeringId).addMember(member.getId());
Expand All @@ -534,11 +541,11 @@ public void testGroupRoleMappingsNotIncludedByDefault() throws Exception {
}

private void enableGroupRoleMappings() {
ClientScopeRepresentation orgScope = managedRealm.admin().clientScopes().findAll().stream()
ClientScopeRepresentation orgScope = realm.admin().clientScopes().findAll().stream()
.filter(s -> OIDCLoginProtocolFactory.ORGANIZATION.equals(s.getName()))
.findAny()
.orElseThrow();
ClientScopeResource orgScopeResource = managedRealm.admin().clientScopes().get(orgScope.getId());
ClientScopeResource orgScopeResource = realm.admin().clientScopes().get(orgScope.getId());

ProtocolMapperRepresentation groupMapper = orgScopeResource.getProtocolMappers().getMappers().stream()
.filter(m -> OrganizationGroupMembershipMapper.PROVIDER_ID.equals(m.getProtocolMapper()))
Expand Down
Loading
Loading