Skip to content

Commit

Permalink
Allow auto-redirect existing users federated from organization broker…
Browse files Browse the repository at this point in the history
… when using the username

Closes keycloak#30746

Signed-off-by: Martin Kanis <mkanis@redhat.com>
  • Loading branch information
martin-kanis authored and stianst committed Jul 25, 2024
1 parent f48ba1d commit 865d33f
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,35 @@ public void action(AuthenticationFlowContext context) {
String username = parameters.getFirst(UserModel.USERNAME);
String emailDomain = getEmailDomain(username);

RealmModel realm = context.getRealm();
OrganizationProvider provider = getOrganizationProvider();
OrganizationModel organization = null;
UserModel user = null;

if (emailDomain == null) {
// username does not map to any email domain, go to the next authentication step/sub-flow
context.attempted();
return;
}
// username was provided, check if the user is already federated in the realm and onboarded in an organization
user = session.users().getUserByUsername(realm, username);
if (user != null) {
organization = getOrganizationProvider().getByMember(user);
}

OrganizationProvider provider = getOrganizationProvider();
OrganizationModel organization = provider.getByDomainName(emailDomain);
if (organization == null) {
// user in not member of an organization, go to the next authentication step/sub-flow
context.attempted();
return;
}
} else {
organization = provider.getByDomainName(emailDomain);
}

if (organization != null) {
// make sure the organization is set to the session to make it available to templates
session.setAttribute(OrganizationModel.class.getName(), organization);
}

RealmModel realm = context.getRealm();
UserModel user = session.users().getUserByEmail(realm, username);
if (user == null) {
user = session.users().getUserByEmail(realm, username);
}

if (user != null) {
// user exists, check if enabled
Expand All @@ -100,8 +113,9 @@ public void action(AuthenticationFlowContext context) {
context.setUser(user);

if (organization != null) {
OrganizationBean orgBean = new OrganizationBean(session, organization, user);
context.form().setAttributeMapper(attributes -> {
attributes.put("org", new OrganizationBean(session, organization, user));
attributes.put("org", orgBean);
return attributes;
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void after() {
public void testFailUnlinkIdentityProvider() throws IOException {
// federate user
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
assertBrokerRegistration(organization, bc.getUserEmail());
assertBrokerRegistration(organization, bc.getUserLogin(), bc.getUserEmail());
// reset password to obtain a token and access the account api
UserRepresentation user = testRealm().users().searchByEmail(bc.getUserEmail(), true).get(0);
ApiUtil.resetUserPassword(realmsResouce().realm(bc.consumerRealmName()).users().get(user.getId()), bc.getUserPassword(), false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,11 @@ protected UserRepresentation addMember(OrganizationResource organization, String
}
}

protected void assertBrokerRegistration(OrganizationResource organization, String email) {
protected void assertBrokerRegistration(OrganizationResource organization, String username, String email) {
// login with email only
openIdentityFirstLoginPage(email, true, null, false, false);

loginOrgIdp(email, email, true, true);
loginOrgIdp(username, email, true, true);

assertIsMember(email, organization);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
@Test
public void testRegistrationRedirectWhenSingleBroker() {
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
assertBrokerRegistration(organization, bc.getUserEmail());
assertBrokerRegistration(organization, bc.getUserLogin(), bc.getUserEmail());
}

@Test
Expand Down Expand Up @@ -276,16 +276,27 @@ public void testRedirectBrokerWhenManagedMember() {
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());

// add the member for the first time
assertBrokerRegistration(organization, bc.getUserEmail());
assertBrokerRegistration(organization, bc.getUserLogin(), bc.getUserEmail());

// logout to force the user to authenticate again
UserRepresentation account = getUserRepresentation(bc.getUserEmail());
realmsResouce().realm(bc.consumerRealmName()).users().get(account.getId()).logout();
realmsResouce().realm(bc.providerRealmName()).logoutAll();

openIdentityFirstLoginPage(bc.getUserLogin(), true, null, false, false);

// login to the organization identity provider by username and automatically redirects to the app as the account already exists
loginPage.login(bc.getUserLogin(), bc.getUserPassword());
appPage.assertCurrent();
assertIsMember(bc.getUserEmail(), organization);

// logout to force the user to authenticate again
realmsResouce().realm(bc.consumerRealmName()).users().get(account.getId()).logout();
realmsResouce().realm(bc.providerRealmName()).logoutAll();

openIdentityFirstLoginPage(bc.getUserEmail(), true, null, false, false);

// login to the organization identity provider and automatically redirects to the app as the account already exists
// login to the organization identity provider by email and automatically redirects to the app as the account already exists
loginPage.login(bc.getUserEmail(), bc.getUserPassword());
appPage.assertCurrent();
assertIsMember(bc.getUserEmail(), organization);
Expand Down Expand Up @@ -316,7 +327,7 @@ public void testShowOnlyBrokersLinkedUserInPasswordPage() {
brokerRep.getConfig().put(IdentityProviderRedirectMode.EMAIL_MATCH.getKey(), Boolean.TRUE.toString());
testRealm().identityProviders().get(brokerRep.getAlias()).update(brokerRep);

assertBrokerRegistration(organization, email);
assertBrokerRegistration(organization, bc.getUserLogin(), email);

// logout to force the user to authenticate again
UserRepresentation account = getUserRepresentation(email);
Expand Down Expand Up @@ -349,7 +360,7 @@ public void testFailUpdateEmailNotAssociatedOrganizationUsingAdminAPI() {
testRealm().identityProviders().get(idpRep.getAlias()).update(idpRep);

// add the member for the first time
assertBrokerRegistration(organization, bc.getUserEmail());
assertBrokerRegistration(organization, bc.getUserLogin(), bc.getUserEmail());
UserRepresentation member = getUserRepresentation(bc.getUserEmail());

member.setEmail(KeycloakModelUtils.generateId() + "@user.org");
Expand All @@ -373,7 +384,7 @@ public void testDeleteManagedMember() {
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());

// add the member for the first time
assertBrokerRegistration(organization, bc.getUserEmail());
assertBrokerRegistration(organization, bc.getUserLogin(), bc.getUserEmail());
UserRepresentation member = getUserRepresentation(bc.getUserEmail());
OrganizationMemberResource organizationMember = organization.members().member(member.getId());

Expand Down Expand Up @@ -635,7 +646,7 @@ public void testMemberFromBrokerRedirectedToOriginBroker() {
public void testAllowUpdateEmailWithDifferentDomainThanOrgIfBrokerHasNoDomainSet() {
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
String email = bc.getUserEmail();
assertBrokerRegistration(organization, email);
assertBrokerRegistration(organization, bc.getUserLogin(), email);

IdentityProviderRepresentation idpRep = organization.identityProviders().getIdentityProviders().get(0);
idpRep.getConfig().remove(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE);
Expand All @@ -649,7 +660,7 @@ public void testAllowUpdateEmailWithDifferentDomainThanOrgIfBrokerHasNoDomainSet
public void testFailUpdateEmailWithDifferentDomainThanOrgIfBrokerHasDomainSet() {
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
String email = bc.getUserEmail();
assertBrokerRegistration(organization, email);
assertBrokerRegistration(organization, bc.getUserLogin(), email);
IdentityProviderRepresentation idpRep = organization.identityProviders().getIdentityProviders().get(0);
assertEquals(email.substring(email.indexOf('@') + 1), idpRep.getConfig().get(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE));
UserRepresentation user = getUserRepresentation(email);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public void testGetAllDisabledOrganization() {
}

// onboard a test user by authenticating using the organization's provider.
super.assertBrokerRegistration(organization, bc.getUserEmail());
super.assertBrokerRegistration(organization, bc.getUserLogin(), bc.getUserEmail());

// disable the organization and check that fetching its representation has it disabled.
orgRep.setEnabled(false);
Expand Down Expand Up @@ -246,7 +246,7 @@ public void testGetAllDisabledOrganizationProvider() throws IOException {
}

// onboard a test user by authenticating using the organization's provider.
super.assertBrokerRegistration(organization, bc.getUserEmail());
super.assertBrokerRegistration(organization, bc.getUserLogin(), bc.getUserEmail());

// now fetch all users from the realm
List<UserRepresentation> members = testRealm().users().search("*neworg*", null, null);
Expand Down

0 comments on commit 865d33f

Please sign in to comment.