Add test for user invite registration and fix minor bug with registration link generation and email templating
Signed-off-by: Alice W <105500542+alice-wondered@users.noreply.github.com>
This commit is contained in:
parent
e0bdb42d41
commit
18356761db
4 changed files with 71 additions and 87 deletions
|
@ -60,6 +60,7 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
|
||||||
protected FreeMarkerProvider freeMarker;
|
protected FreeMarkerProvider freeMarker;
|
||||||
protected RealmModel realm;
|
protected RealmModel realm;
|
||||||
protected UserModel user;
|
protected UserModel user;
|
||||||
|
protected String userEmail;
|
||||||
protected final Map<String, Object> attributes = new HashMap<>();
|
protected final Map<String, Object> attributes = new HashMap<>();
|
||||||
|
|
||||||
public FreeMarkerEmailTemplateProvider(KeycloakSession session) {
|
public FreeMarkerEmailTemplateProvider(KeycloakSession session) {
|
||||||
|
|
|
@ -63,6 +63,7 @@ import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
import org.keycloak.services.resources.admin.UserResource;
|
import org.keycloak.services.resources.admin.UserResource;
|
||||||
import org.keycloak.services.resources.admin.UsersResource;
|
import org.keycloak.services.resources.admin.UsersResource;
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||||
|
import org.keycloak.storage.adapter.InMemoryUserAdapter;
|
||||||
import org.keycloak.utils.StringUtil;
|
import org.keycloak.utils.StringUtil;
|
||||||
|
|
||||||
@Provider
|
@Provider
|
||||||
|
@ -125,9 +126,6 @@ public class OrganizationMemberResource {
|
||||||
UserModel user = session.users().getUserByEmail(realm, rep.getEmail());
|
UserModel user = session.users().getUserByEmail(realm, rep.getEmail());
|
||||||
|
|
||||||
InviteOrgActionToken token = null;
|
InviteOrgActionToken token = null;
|
||||||
// TODO not sure if this client id is right or if we should get one from the user somehow...
|
|
||||||
// TODO not really sure if the token is getting signed so we need to figure out where that's happening... maybe in the serialize method?
|
|
||||||
// TODO the expiration is set to a day in seconds but we should probably get this from configuration instead
|
|
||||||
String link = null;
|
String link = null;
|
||||||
int tokenExpiration = Time.currentTime() + realm.getActionTokenGeneratedByAdminLifespan();
|
int tokenExpiration = Time.currentTime() + realm.getActionTokenGeneratedByAdminLifespan();
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
|
@ -138,13 +136,18 @@ public class OrganizationMemberResource {
|
||||||
.build(realm.getName()).toString();
|
.build(realm.getName()).toString();
|
||||||
} else {
|
} else {
|
||||||
// this path lets us invite a user that doesn't exist yet, letting them register into the organization
|
// this path lets us invite a user that doesn't exist yet, letting them register into the organization
|
||||||
token = new InviteOrgActionToken(null, tokenExpiration, rep.getEmail(), session.getContext().getClient().getClientId());
|
token = new InviteOrgActionToken(null, tokenExpiration, rep.getEmail(), Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
|
||||||
token.setOrgId(organization.getId());
|
token.setOrgId(organization.getId());
|
||||||
link = LoginActionsService.registrationFormProcessor(session.getContext().getUri())
|
link = LoginActionsService.registrationFormProcessor(session.getContext().getUri())
|
||||||
.queryParam(Constants.ORG_TOKEN, token.serialize(session, realm, session.getContext().getUri()))
|
.queryParam(Constants.ORG_TOKEN, token.serialize(session, realm, session.getContext().getUri()))
|
||||||
.build(realm.getName()).toString();
|
.build(realm.getName()).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user == null ) {
|
||||||
|
user = new InMemoryUserAdapter(session, realm, null);
|
||||||
|
user.setEmail(rep.getEmail());
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
session
|
session
|
||||||
.getProvider(EmailTemplateProvider.class)
|
.getProvider(EmailTemplateProvider.class)
|
||||||
|
|
|
@ -19,9 +19,12 @@ package org.keycloak.testsuite.organization.admin;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@ -31,13 +34,23 @@ import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.OrganizationResource;
|
import org.keycloak.admin.client.resource.OrganizationResource;
|
||||||
|
import org.keycloak.authentication.requiredactions.TermsAndConditions;
|
||||||
import org.keycloak.common.Profile.Feature;
|
import org.keycloak.common.Profile.Feature;
|
||||||
|
import org.keycloak.common.util.Time;
|
||||||
|
import org.keycloak.common.util.UriUtils;
|
||||||
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
import org.keycloak.testsuite.pages.InfoPage;
|
import org.keycloak.testsuite.pages.InfoPage;
|
||||||
|
import org.keycloak.testsuite.pages.RegisterPage;
|
||||||
import org.keycloak.testsuite.util.GreenMailRule;
|
import org.keycloak.testsuite.util.GreenMailRule;
|
||||||
import org.keycloak.testsuite.util.MailUtils;
|
import org.keycloak.testsuite.util.MailUtils;
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
|
@ -45,12 +58,18 @@ import org.keycloak.testsuite.util.UserBuilder;
|
||||||
@EnableFeature(Feature.ORGANIZATION)
|
@EnableFeature(Feature.ORGANIZATION)
|
||||||
public class OrganizationInvitationLinkTest extends AbstractOrganizationTest {
|
public class OrganizationInvitationLinkTest extends AbstractOrganizationTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public AssertEvents events = new AssertEvents(this);
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public GreenMailRule greenMail = new GreenMailRule();
|
public GreenMailRule greenMail = new GreenMailRule();
|
||||||
|
|
||||||
@Page
|
@Page
|
||||||
protected InfoPage infoPage;
|
protected InfoPage infoPage;
|
||||||
|
|
||||||
|
@Page
|
||||||
|
protected RegisterPage registerPage;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
Map<String, String> smtpConfig = testRealm.getSmtpServer();
|
Map<String, String> smtpConfig = testRealm.getSmtpServer();
|
||||||
|
@ -84,4 +103,48 @@ public class OrganizationInvitationLinkTest extends AbstractOrganizationTest {
|
||||||
assertThat(infoPage.getInfo(), containsString("Your account has been updated."));
|
assertThat(infoPage.getInfo(), containsString("Your account has been updated."));
|
||||||
Assert.assertTrue(organization.members().getAll().stream().anyMatch(actual -> user.getId().equals(actual.getId())));
|
Assert.assertTrue(organization.members().getAll().stream().anyMatch(actual -> user.getId().equals(actual.getId())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInviteNewUserRegistration() throws IOException {
|
||||||
|
|
||||||
|
UserRepresentation user = UserBuilder.create()
|
||||||
|
.username("invitedUser")
|
||||||
|
.email("inviteduser@email")
|
||||||
|
.enabled(true)
|
||||||
|
.build();
|
||||||
|
// User isn't created when we send the invite
|
||||||
|
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||||
|
organization.members().inviteMember(user).close();
|
||||||
|
|
||||||
|
MimeMessage message = greenMail.getLastReceivedMessage();
|
||||||
|
Assert.assertNotNull(message);
|
||||||
|
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||||
|
String orgToken = UriUtils.parseQueryParameters(link, false).values().stream().map(strings -> strings.get(0)).findFirst().orElse(null);
|
||||||
|
Assert.assertNotNull(orgToken);
|
||||||
|
|
||||||
|
driver.manage().timeouts().pageLoadTimeout(1, TimeUnit.DAYS);
|
||||||
|
driver.navigate().to(link.trim());
|
||||||
|
Assert.assertFalse(organization.members().getAll().stream().anyMatch(actual -> user.getId().equals(actual.getId())));
|
||||||
|
|
||||||
|
registerPage.assertCurrent();
|
||||||
|
registerPage.register("firstName", "lastName", "inviteduser@myemail",
|
||||||
|
"invitedUser", "password", "password", null, true, null);
|
||||||
|
|
||||||
|
assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
|
String userId = events.expectRegister("invitedUser", "inviteduser@email")
|
||||||
|
.assertEvent().getUserId();
|
||||||
|
UserRepresentation registeredUser = assertUserRegistered(userId, "invitedUser");
|
||||||
|
Assert.assertTrue(organization.members().getAll().stream().anyMatch(actual -> registeredUser.getId().equals(actual.getId())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserRepresentation assertUserRegistered(String userId, String username) {
|
||||||
|
events.expectLogin().detail("username", username.toLowerCase()).user(userId).assertEvent();
|
||||||
|
|
||||||
|
UserRepresentation user = testRealm().users().get(userId).toRepresentation();
|
||||||
|
org.junit.Assert.assertNotNull(user);
|
||||||
|
org.junit.Assert.assertNotNull(user.getCreatedTimestamp());
|
||||||
|
return user;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -33,7 +33,6 @@ import static org.keycloak.models.OrganizationModel.ORGANIZATION_ATTRIBUTE;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jakarta.ws.rs.BadRequestException;
|
import jakarta.ws.rs.BadRequestException;
|
||||||
import jakarta.ws.rs.NotFoundException;
|
import jakarta.ws.rs.NotFoundException;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
|
@ -296,86 +295,4 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
assertFalse(testRealm().groups().groups().stream().anyMatch(group -> group.getName().startsWith("kc.org.")));
|
assertFalse(testRealm().groups().groups().stream().anyMatch(group -> group.getName().startsWith("kc.org.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSearchMembers() {
|
|
||||||
|
|
||||||
// create test users, ordered by username (e-mail).
|
|
||||||
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
|
||||||
List<UserRepresentation> expected = new ArrayList<>();
|
|
||||||
expected.add(addMember(organization, "batwoman@neworg.org", "Katherine", "Kane"));
|
|
||||||
expected.add(addMember(organization, "brucewayne@neworg.org", "Bruce", "Wayne"));
|
|
||||||
expected.add(addMember(organization, "harveydent@neworg.org", "Harvey", "Dent"));
|
|
||||||
expected.add(addMember(organization, "marthaw@neworg.org", "Martha", "Wayne"));
|
|
||||||
expected.add(addMember(organization, "thejoker@neworg.org", "Jack", "White"));
|
|
||||||
|
|
||||||
// exact search - username/e-mail, first name, last name.
|
|
||||||
List<UserRepresentation> existing = organization.members().search("brucewayne@neworg.org", true, null, null);
|
|
||||||
assertThat(existing, hasSize(1));
|
|
||||||
assertThat(existing.get(0).getUsername(), is(equalTo("brucewayne@neworg.org")));
|
|
||||||
assertThat(existing.get(0).getEmail(), is(equalTo("brucewayne@neworg.org")));
|
|
||||||
assertThat(existing.get(0).getFirstName(), is(equalTo("Bruce")));
|
|
||||||
assertThat(existing.get(0).getLastName(), is(equalTo("Wayne")));
|
|
||||||
|
|
||||||
existing = organization.members().search("Harvey", true, null, null);
|
|
||||||
assertThat(existing, hasSize(1));
|
|
||||||
assertThat(existing.get(0).getUsername(), is(equalTo("harveydent@neworg.org")));
|
|
||||||
assertThat(existing.get(0).getEmail(), is(equalTo("harveydent@neworg.org")));
|
|
||||||
assertThat(existing.get(0).getFirstName(), is(equalTo("Harvey")));
|
|
||||||
assertThat(existing.get(0).getLastName(), is(equalTo("Dent")));
|
|
||||||
|
|
||||||
existing = organization.members().search("Wayne", true, null, null);
|
|
||||||
assertThat(existing, hasSize(2));
|
|
||||||
assertThat(existing.get(0).getUsername(), is(equalTo("brucewayne@neworg.org")));
|
|
||||||
assertThat(existing.get(1).getUsername(), is(equalTo("marthaw@neworg.org")));
|
|
||||||
|
|
||||||
existing = organization.members().search("Gordon", true, null, null);
|
|
||||||
assertThat(existing, is(empty()));
|
|
||||||
|
|
||||||
// partial search - partial e-mail should match all users.
|
|
||||||
existing = organization.members().search("neworg", false, null, null);
|
|
||||||
assertThat(existing, hasSize(5));
|
|
||||||
for (int i = 0; i < 5; i++) { // returned entries should also be ordered.
|
|
||||||
assertThat(expected.get(i).getId(), is(equalTo(expected.get(i).getId())));
|
|
||||||
assertThat(expected.get(i).getUsername(), is(equalTo(expected.get(i).getUsername())));
|
|
||||||
assertThat(expected.get(i).getEmail(), is(equalTo(expected.get(i).getEmail())));
|
|
||||||
assertThat(expected.get(i).getFirstName(), is(equalTo(expected.get(i).getFirstName())));
|
|
||||||
assertThat(expected.get(i).getLastName(), is(equalTo(expected.get(i).getLastName())));
|
|
||||||
}
|
|
||||||
|
|
||||||
// partial search using 'th' search string - should match 'Katherine' by name, 'Jack' by username/e-mail
|
|
||||||
// and 'Martha' either by username or first name.
|
|
||||||
existing = organization.members().search("th", false, null, null);
|
|
||||||
assertThat(existing, hasSize(3));
|
|
||||||
assertThat(existing.get(0).getUsername(), is(equalTo("batwoman@neworg.org")));
|
|
||||||
assertThat(existing.get(0).getFirstName(), is(equalTo("Katherine")));
|
|
||||||
assertThat(existing.get(1).getUsername(), is(equalTo("marthaw@neworg.org")));
|
|
||||||
assertThat(existing.get(1).getFirstName(), is(equalTo("Martha")));
|
|
||||||
assertThat(existing.get(2).getUsername(), is(equalTo("thejoker@neworg.org")));
|
|
||||||
assertThat(existing.get(2).getFirstName(), is(equalTo("Jack")));
|
|
||||||
|
|
||||||
// partial search using 'way' - should match both 'Bruce' (either by username or last name) and 'Martha' by last name.
|
|
||||||
existing = organization.members().search("way", false, null, null);
|
|
||||||
assertThat(existing, hasSize(2));
|
|
||||||
assertThat(existing.get(0).getUsername(), is(equalTo("brucewayne@neworg.org")));
|
|
||||||
assertThat(existing.get(0).getFirstName(), is(equalTo("Bruce")));
|
|
||||||
assertThat(existing.get(1).getUsername(), is(equalTo("marthaw@neworg.org")));
|
|
||||||
assertThat(existing.get(1).getFirstName(), is(equalTo("Martha")));
|
|
||||||
|
|
||||||
// partial search using with no match - e.g. 'nonexistent'.
|
|
||||||
existing = organization.members().search("nonexistent", false, null, null);
|
|
||||||
assertThat(existing, is(empty()));
|
|
||||||
|
|
||||||
// paginated search - try to fetch 3 users per page.
|
|
||||||
existing = organization.members().search("", false, 0, 3);
|
|
||||||
assertThat(existing, hasSize(3));
|
|
||||||
assertThat(existing.get(0).getUsername(), is(equalTo("batwoman@neworg.org")));
|
|
||||||
assertThat(existing.get(1).getUsername(), is(equalTo("brucewayne@neworg.org")));
|
|
||||||
assertThat(existing.get(2).getUsername(), is(equalTo("harveydent@neworg.org")));
|
|
||||||
|
|
||||||
existing = organization.members().search("", false, 3, 3);
|
|
||||||
assertThat(existing, hasSize(2));
|
|
||||||
assertThat(existing.get(0).getUsername(), is(equalTo("marthaw@neworg.org")));
|
|
||||||
assertThat(existing.get(1).getUsername(), is(equalTo("thejoker@neworg.org")));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue