Filter out org brokers from the account console
- org-linked brokers should not be available for login - prepare the endpoint for search/pagination Closes #31944 Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
This commit is contained in:
parent
4d1e1e0bcb
commit
e7a4635620
5 changed files with 239 additions and 83 deletions
|
@ -29,7 +29,7 @@ public class LinkedAccountRepresentation implements Comparable<LinkedAccountRepr
|
|||
private String providerName;
|
||||
private String displayName;
|
||||
private String linkedUsername;
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
private String guiOrder;
|
||||
|
||||
|
@ -48,7 +48,7 @@ public class LinkedAccountRepresentation implements Comparable<LinkedAccountRepr
|
|||
public void setConnected(boolean connected) {
|
||||
this.connected = connected;
|
||||
}
|
||||
|
||||
|
||||
public boolean isSocial() {
|
||||
return this.isSocial;
|
||||
}
|
||||
|
@ -91,10 +91,10 @@ public class LinkedAccountRepresentation implements Comparable<LinkedAccountRepr
|
|||
|
||||
@Override
|
||||
public int compareTo(LinkedAccountRepresentation rep) {
|
||||
if (this.getGuiOrder() == null) return -1;
|
||||
if (rep.getGuiOrder() == null) return 1;
|
||||
|
||||
return rep.getGuiOrder().compareTo(this.getGuiOrder());
|
||||
if (this.getGuiOrder() == null) return 1;
|
||||
if (rep.getGuiOrder() == null) return -1;
|
||||
|
||||
return Integer.valueOf(this.getGuiOrder()).compareTo(Integer.valueOf(rep.getGuiOrder()));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.keycloak.models.jpa;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -49,6 +50,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
|||
import org.keycloak.utils.StringUtil;
|
||||
|
||||
import static org.keycloak.models.IdentityProviderModel.ALIAS;
|
||||
import static org.keycloak.models.IdentityProviderModel.ALIAS_NOT_IN;
|
||||
import static org.keycloak.models.IdentityProviderModel.AUTHENTICATE_BY_DEFAULT;
|
||||
import static org.keycloak.models.IdentityProviderModel.ENABLED;
|
||||
import static org.keycloak.models.IdentityProviderModel.FIRST_BROKER_LOGIN_FLOW_ID;
|
||||
|
@ -254,7 +256,15 @@ public class JpaIdentityProviderStorageProvider implements IdentityProviderStora
|
|||
predicates.add(this.getAliasSearchPredicate(value, builder, idp));
|
||||
}
|
||||
break;
|
||||
} default: {
|
||||
}
|
||||
case ALIAS_NOT_IN: {
|
||||
if (StringUtil.isNotBlank(value)) {
|
||||
List<String> aliases = Arrays.asList(value.split(","));
|
||||
predicates.add(builder.not(idp.get(ALIAS).in(aliases)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
String dbProductName = em.unwrap(Session.class).doReturningWork(connection -> connection.getMetaData().getDatabaseProductName());
|
||||
MapJoin<IdentityProviderEntity, String, String> configJoin = idp.joinMap("config");
|
||||
Predicate configNamePredicate = builder.equal(configJoin.key(), key);
|
||||
|
|
|
@ -32,6 +32,7 @@ import java.util.Objects;
|
|||
public class IdentityProviderModel implements Serializable {
|
||||
|
||||
public static final String ALIAS = "alias";
|
||||
public static final String ALIAS_NOT_IN = "aliasNotIn";
|
||||
public static final String ALLOWED_CLOCK_SKEW = "allowedClockSkew";
|
||||
public static final String AUTHENTICATE_BY_DEFAULT = "authenticateByDefault";
|
||||
public static final String CASE_SENSITIVE_ORIGINAL_USERNAME = "caseSensitiveOriginalUsername";
|
||||
|
|
|
@ -20,11 +20,10 @@ import java.io.IOException;
|
|||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -66,6 +65,7 @@ import org.keycloak.services.managers.Auth;
|
|||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.validation.Validation;
|
||||
import org.keycloak.theme.Theme;
|
||||
import org.keycloak.utils.StreamsUtil;
|
||||
|
||||
import static org.keycloak.models.Constants.ACCOUNT_CONSOLE_CLIENT_ID;
|
||||
|
||||
|
@ -83,6 +83,7 @@ public class LinkedAccountsResource {
|
|||
private final UserModel user;
|
||||
private final RealmModel realm;
|
||||
private final Auth auth;
|
||||
private final Set<String> socialIds;
|
||||
|
||||
public LinkedAccountsResource(KeycloakSession session,
|
||||
HttpRequest request,
|
||||
|
@ -94,36 +95,117 @@ public class LinkedAccountsResource {
|
|||
this.auth = auth;
|
||||
this.event = event;
|
||||
this.user = user;
|
||||
realm = session.getContext().getRealm();
|
||||
this.realm = session.getContext().getRealm();
|
||||
this.socialIds = session.getKeycloakSessionFactory().getProviderFactoriesStream(SocialIdentityProvider.class)
|
||||
.map(ProviderFactory::getId)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the enabled identity providers the user is currently linked to, or those available for the user to link their
|
||||
* account to.When the {@code linked} param is {@code true}, all providers currently linked to the user are returned in
|
||||
* the form of {@link LinkedAccountRepresentation} objects, including those associated with organizations.
|
||||
* </p>
|
||||
* When the {@code linked} param is {@code false}, only the identity providers not linked to organizations (i.e. realm
|
||||
* level providers) will be returned and be made available for linking.
|
||||
*
|
||||
* @param linked a {@link Boolean} indicating whether to return only the linked providers ({@code true}) or only the
|
||||
* providers available for linking ({@code false}).
|
||||
* @param search Filter to search specific providers by name. Search can be prefixed (name*), contains (*name*) or exact (\"name\"). Default prefixed.
|
||||
* @param firstResult Pagination offset.
|
||||
* @param maxResults Maximum results size.
|
||||
* @return a set of {@link LinkedAccountRepresentation} sorted by the {code guiOrder}.
|
||||
*/
|
||||
@GET
|
||||
@Path("/")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response linkedAccounts() {
|
||||
public Response linkedAccounts(
|
||||
@QueryParam("linked") Boolean linked,
|
||||
@QueryParam("search") String search,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResults
|
||||
) {
|
||||
auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE);
|
||||
SortedSet<LinkedAccountRepresentation> linkedAccounts = getLinkedAccounts(this.session, this.realm, this.user);
|
||||
|
||||
// TODO: remove this statement once the console and the LinkedAccountsRestServiceTest are updated - this is only here for backwards compatibility
|
||||
if (linked == null) {
|
||||
List<LinkedAccountRepresentation> linkedAccounts = getLinkedAccounts(this.session, this.realm, this.user);
|
||||
return Cors.builder().auth().allowedOrigins(auth.getToken()).add(Response.ok(linkedAccounts));
|
||||
}
|
||||
|
||||
List<LinkedAccountRepresentation> linkedAccounts;
|
||||
if (linked) {
|
||||
// we want only linked accounts, fetch those from the federated identities.
|
||||
linkedAccounts = StreamsUtil.paginatedStream(session.users().getFederatedIdentitiesStream(realm, user)
|
||||
.map(fedIdentity -> this.toLinkedAccount(session.identityProviders().getByAlias(fedIdentity.getIdentityProvider()), fedIdentity.getUserName()))
|
||||
.filter(account -> account != null && this.matchesLinkedProvider(account, search))
|
||||
.sorted(), firstResult, maxResults)
|
||||
.toList();
|
||||
} else {
|
||||
// we want all enabled, realm-level identity providers available (i.e. not already linked) for the user to link their accounts to.
|
||||
String fedAliasesToExclude = session.users().getFederatedIdentitiesStream(realm, user).map(FederatedIdentityModel::getIdentityProvider)
|
||||
.collect(Collectors.joining(","));
|
||||
|
||||
Map<String, String> searchOptions = Map.of(
|
||||
IdentityProviderModel.ENABLED, "true",
|
||||
IdentityProviderModel.ORGANIZATION_ID, "",
|
||||
IdentityProviderModel.SEARCH, search == null ? "" : search,
|
||||
IdentityProviderModel.ALIAS_NOT_IN, fedAliasesToExclude);
|
||||
|
||||
linkedAccounts = session.identityProviders().getAllStream(searchOptions, firstResult, maxResults)
|
||||
.map(idp -> this.toLinkedAccount(idp, null))
|
||||
.toList();
|
||||
}
|
||||
return Cors.builder().auth().allowedOrigins(auth.getToken()).add(Response.ok(linkedAccounts));
|
||||
}
|
||||
|
||||
private Set<String> findSocialIds() {
|
||||
return session.getKeycloakSessionFactory().getProviderFactoriesStream(SocialIdentityProvider.class)
|
||||
.map(ProviderFactory::getId)
|
||||
.collect(Collectors.toSet());
|
||||
private LinkedAccountRepresentation toLinkedAccount(IdentityProviderModel provider, String fedIdentity) {
|
||||
if (provider == null || !provider.isEnabled()) {
|
||||
return null;
|
||||
}
|
||||
LinkedAccountRepresentation rep = new LinkedAccountRepresentation();
|
||||
rep.setConnected(fedIdentity != null);
|
||||
rep.setSocial(socialIds.contains(provider.getProviderId()));
|
||||
rep.setProviderAlias(provider.getAlias());
|
||||
rep.setDisplayName(KeycloakModelUtils.getIdentityProviderDisplayName(session, provider));
|
||||
rep.setGuiOrder(provider.getConfig() != null ? provider.getConfig().get("guiOrder") : null);
|
||||
rep.setProviderName(provider.getAlias());
|
||||
rep.setLinkedUsername(fedIdentity);
|
||||
return rep;
|
||||
}
|
||||
|
||||
public SortedSet<LinkedAccountRepresentation> getLinkedAccounts(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
Set<String> socialIds = findSocialIds();
|
||||
private boolean matchesLinkedProvider(final LinkedAccountRepresentation linkedAccount, final String search) {
|
||||
if (search == null) {
|
||||
return true;
|
||||
}else if (search.startsWith("\"") && search.endsWith("\"")) {
|
||||
final String name = search.substring(1, search.length() - 1);
|
||||
return linkedAccount.getProviderAlias().equals(name);
|
||||
} else if (search.startsWith("*") && search.endsWith("*")) {
|
||||
final String name = search.substring(1, search.length() - 1);
|
||||
return linkedAccount.getProviderAlias().contains(name);
|
||||
} else if (search.endsWith("*")) {
|
||||
final String name = search.substring(0, search.length() - 1);
|
||||
return linkedAccount.getProviderAlias().startsWith(name);
|
||||
} else {
|
||||
return linkedAccount.getProviderAlias().startsWith(search);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public List<LinkedAccountRepresentation> getLinkedAccounts(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
return session.identityProviders().getAllStream(Map.of(IdentityProviderModel.ENABLED, "true"), null, null)
|
||||
.map(provider -> toLinkedAccountRepresentation(provider, socialIds, session.users().getFederatedIdentitiesStream(realm, user)))
|
||||
.collect(Collectors.toCollection(TreeSet::new));
|
||||
.map(provider -> toLinkedAccountRepresentation(provider, session.users().getFederatedIdentitiesStream(realm, user)))
|
||||
.filter(Objects::nonNull)
|
||||
.sorted().toList();
|
||||
}
|
||||
|
||||
private LinkedAccountRepresentation toLinkedAccountRepresentation(IdentityProviderModel provider, Set<String> socialIds,
|
||||
Stream<FederatedIdentityModel> identities) {
|
||||
@Deprecated
|
||||
private LinkedAccountRepresentation toLinkedAccountRepresentation(IdentityProviderModel provider, Stream<FederatedIdentityModel> identities) {
|
||||
String providerAlias = provider.getAlias();
|
||||
|
||||
FederatedIdentityModel identity = getIdentity(identities, providerAlias);
|
||||
// if idp is not yet linked and is currently bound to an organization, it should not be available for linking.
|
||||
if (identity == null && provider.getOrganizationId() != null) return null;
|
||||
|
||||
String displayName = KeycloakModelUtils.getIdentityProviderDisplayName(session, provider);
|
||||
String guiOrder = provider.getConfig() != null ? provider.getConfig().get("guiOrder") : null;
|
||||
|
@ -141,6 +223,7 @@ public class LinkedAccountsResource {
|
|||
return rep;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private FederatedIdentityModel getIdentity(Stream<FederatedIdentityModel> identities, String providerAlias) {
|
||||
return identities.filter(model -> Objects.equals(model.getIdentityProvider(), providerAlias))
|
||||
.findFirst().orElse(null);
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.junit.After;
|
|||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
|
@ -37,12 +36,15 @@ import java.nio.charset.Charset;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.utils.URLEncodedUtils;
|
||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.util.IdentityProviderBuilder;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
@ -86,81 +88,78 @@ public class LinkedAccountsRestServiceTest extends AbstractTestRealmKeycloakTest
|
|||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
testRealm.getUsers().add(UserBuilder.create().username("no-account-access").password("password").build());
|
||||
testRealm.getUsers().add(UserBuilder.create().username("view-account-access").role("account", "view-profile").password("password").build());
|
||||
|
||||
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
|
||||
.providerId("github")
|
||||
.alias("github")
|
||||
.setAttribute("guiOrder", "2")
|
||||
.build());
|
||||
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
|
||||
.providerId("saml")
|
||||
.alias("mysaml")
|
||||
.setAttribute("guiOrder", "0")
|
||||
.build());
|
||||
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
|
||||
.providerId("oidc")
|
||||
.alias("myoidc")
|
||||
.displayName("MyOIDC")
|
||||
.setAttribute("guiOrder", "1")
|
||||
.build());
|
||||
|
||||
addGitHubIdentity(testRealm);
|
||||
}
|
||||
|
||||
private void addGitHubIdentity(RealmRepresentation testRealm) {
|
||||
UserRepresentation acctMgtUser = findUser(testRealm, "test-user@localhost");
|
||||
|
||||
FederatedIdentityRepresentation fedIdp = new FederatedIdentityRepresentation();
|
||||
fedIdp.setIdentityProvider("github");
|
||||
fedIdp.setUserId("foo");
|
||||
fedIdp.setUserName("foo");
|
||||
|
||||
ArrayList<FederatedIdentityRepresentation> fedIdps = new ArrayList<>();
|
||||
fedIdps.add(fedIdp);
|
||||
|
||||
acctMgtUser.setFederatedIdentities(fedIdps);
|
||||
String[] providers = new String[]{"saml:mysaml", "oidc:myoidc", "github", "gitlab", "twitter", "facebook", "bitbucket", "microsoft"};
|
||||
for (int i = 0; i < providers.length; i++) {
|
||||
String[] idpInfo = providers[i].split(":");
|
||||
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
|
||||
.providerId(idpInfo[0])
|
||||
.alias(idpInfo.length == 1 ? idpInfo[0] : idpInfo[1])
|
||||
.setAttribute("guiOrder", String.valueOf(i))
|
||||
.build());
|
||||
}
|
||||
|
||||
addFederatedIdentities(testRealm, "github", "gitlab", "twitter");
|
||||
}
|
||||
|
||||
|
||||
private void addFederatedIdentities(RealmRepresentation testRealm, String... idpAliases) {
|
||||
UserRepresentation acctMgtUser = findUser(testRealm, "test-user@localhost");
|
||||
if (acctMgtUser != null) {
|
||||
ArrayList<FederatedIdentityRepresentation> fedIdps = new ArrayList<>();
|
||||
for (String alias : idpAliases) {
|
||||
FederatedIdentityRepresentation fedIdp = new FederatedIdentityRepresentation();
|
||||
fedIdp.setIdentityProvider(alias);
|
||||
fedIdp.setUserId("foo");
|
||||
fedIdp.setUserName("foo");
|
||||
fedIdps.add(fedIdp);
|
||||
}
|
||||
acctMgtUser.setFederatedIdentities(fedIdps);
|
||||
}
|
||||
}
|
||||
|
||||
private UserRepresentation findUser(RealmRepresentation testRealm, String userName) {
|
||||
for (UserRepresentation user : testRealm.getUsers()) {
|
||||
if (user.getUsername().equals(userName)) return user;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getAccountUrl(String resource) {
|
||||
return suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth/realms/test/account" + (resource != null ? "/" + resource : "");
|
||||
}
|
||||
|
||||
private SortedSet<LinkedAccountRepresentation> linkedAccountsRep() throws IOException {
|
||||
return SimpleHttpDefault.doGet(getAccountUrl("linked-accounts"), client).auth(tokenUtil.getToken()).asJson(new TypeReference<SortedSet<LinkedAccountRepresentation>>() {});
|
||||
|
||||
private List<LinkedAccountRepresentation> linkedAccountsRep() throws IOException {
|
||||
return SimpleHttpDefault.doGet(getAccountUrl("linked-accounts"), client).auth(tokenUtil.getToken()).asJson(new TypeReference<>() {});
|
||||
}
|
||||
|
||||
|
||||
private List<LinkedAccountRepresentation> linkedAccountsRep(String params) throws IOException {
|
||||
return SimpleHttpDefault.doGet(getAccountUrl("linked-accounts?" + params), client).auth(tokenUtil.getToken()).asJson(new TypeReference<>() {});
|
||||
}
|
||||
|
||||
private LinkedAccountRepresentation findLinkedAccount(String providerAlias) throws IOException {
|
||||
for (LinkedAccountRepresentation account : linkedAccountsRep()) {
|
||||
if (account.getProviderAlias().equals(providerAlias)) return account;
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
|
||||
|
||||
public void testBuildLinkedAccountUri() throws IOException {
|
||||
AccountLinkUriRepresentation rep = SimpleHttpDefault.doGet(getAccountUrl("linked-accounts/github?redirectUri=phonyUri"), client)
|
||||
.auth(tokenUtil.getToken())
|
||||
.asJson(new TypeReference<AccountLinkUriRepresentation>() {});
|
||||
URI brokerUri = rep.getAccountLinkUri();
|
||||
|
||||
|
||||
assertTrue(brokerUri.getPath().endsWith("/auth/realms/test/broker/github/link"));
|
||||
|
||||
|
||||
List<NameValuePair> queryParams = URLEncodedUtils.parse(brokerUri, Charset.defaultCharset());
|
||||
assertEquals(4, queryParams.size());
|
||||
for (NameValuePair nvp : queryParams) {
|
||||
switch (nvp.getName()) {
|
||||
case "nonce" : {
|
||||
assertNotNull(nvp.getValue());
|
||||
case "nonce" : {
|
||||
assertNotNull(nvp.getValue());
|
||||
assertEquals(rep.getNonce(), nvp.getValue());
|
||||
break;
|
||||
}
|
||||
|
@ -174,33 +173,96 @@ public class LinkedAccountsRestServiceTest extends AbstractTestRealmKeycloakTest
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetLinkedAccounts() throws IOException {
|
||||
SortedSet<LinkedAccountRepresentation> details = linkedAccountsRep();
|
||||
assertEquals(3, details.size());
|
||||
|
||||
int order = 0;
|
||||
List<LinkedAccountRepresentation> details = linkedAccountsRep();
|
||||
assertEquals(8, details.size());
|
||||
|
||||
// test order of linked accounts
|
||||
List<String> linkedAccountAliases = details.stream().map(LinkedAccountRepresentation::getProviderAlias).toList();
|
||||
assertThat(linkedAccountAliases, contains("mysaml", "myoidc", "github", "gitlab", "twitter", "facebook", "bitbucket", "microsoft"));
|
||||
|
||||
List<String> expectedConnectedAccounts = List.of("github", "gitlab", "twitter");
|
||||
for (LinkedAccountRepresentation account : details) {
|
||||
if (account.getProviderAlias().equals("github")) {
|
||||
if (expectedConnectedAccounts.contains(account.getProviderAlias())) {
|
||||
assertTrue(account.isConnected());
|
||||
} else {
|
||||
assertFalse(account.isConnected());
|
||||
}
|
||||
|
||||
// test that accounts were sorted by guiOrder
|
||||
if (order == 0) assertEquals("mysaml", account.getDisplayName());
|
||||
if (order == 1) assertEquals("MyOIDC", account.getDisplayName());
|
||||
if (order == 2) assertEquals("GitHub", account.getDisplayName());
|
||||
order++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetLinkedAccountsWithPagination() throws IOException {
|
||||
|
||||
// search only connected accounts, with a max result size of 10 - should fetch all connected accounts.
|
||||
List<LinkedAccountRepresentation> accounts = linkedAccountsRep("linked=true&first=0&max=10");
|
||||
assertEquals(3, accounts.size());
|
||||
|
||||
List<String> linkedAccountAliases = accounts.stream().map(LinkedAccountRepresentation::getProviderAlias).toList();
|
||||
assertThat(linkedAccountAliases, contains("github", "gitlab", "twitter"));
|
||||
for (LinkedAccountRepresentation account : accounts) {
|
||||
assertTrue(account.isConnected());
|
||||
}
|
||||
|
||||
// same search, but testing the pagination.
|
||||
accounts = linkedAccountsRep("linked=true&first=0&max=2");
|
||||
assertEquals(2, accounts.size());
|
||||
linkedAccountAliases = accounts.stream().map(LinkedAccountRepresentation::getProviderAlias).toList();
|
||||
assertThat(linkedAccountAliases, contains("github", "gitlab"));
|
||||
|
||||
accounts = linkedAccountsRep("linked=true&first=2&max=4");
|
||||
assertEquals(1, accounts.size());
|
||||
linkedAccountAliases = accounts.stream().map(LinkedAccountRepresentation::getProviderAlias).toList();
|
||||
assertThat(linkedAccountAliases, contains("twitter"));
|
||||
|
||||
// now use a search string to further filter the results.
|
||||
accounts = linkedAccountsRep("linked=true&search=git*");
|
||||
assertEquals(2, accounts.size());
|
||||
linkedAccountAliases = accounts.stream().map(LinkedAccountRepresentation::getProviderAlias).toList();
|
||||
assertThat(linkedAccountAliases, contains("github", "gitlab"));
|
||||
|
||||
// search unlinked identity providers.
|
||||
accounts = linkedAccountsRep("linked=false&first=0&max=10");
|
||||
assertEquals(5, accounts.size());
|
||||
|
||||
linkedAccountAliases = accounts.stream().map(LinkedAccountRepresentation::getProviderAlias).toList();
|
||||
// the unlinked accounts are ordered by alias, not gui order (this test needs to be adjusted if the model is fixed to order by gui order)
|
||||
assertThat(linkedAccountAliases, contains("bitbucket", "facebook", "microsoft", "myoidc", "mysaml"));
|
||||
for (LinkedAccountRepresentation account : accounts) {
|
||||
assertFalse(account.isConnected());
|
||||
}
|
||||
|
||||
// same search, but testing the pagination.
|
||||
accounts = linkedAccountsRep("linked=false&first=0&max=3");
|
||||
assertEquals(3, accounts.size());
|
||||
linkedAccountAliases = accounts.stream().map(LinkedAccountRepresentation::getProviderAlias).toList();
|
||||
assertThat(linkedAccountAliases, contains("bitbucket", "facebook", "microsoft"));
|
||||
|
||||
accounts = linkedAccountsRep("linked=false&first=3&max=3");
|
||||
assertEquals(2, accounts.size());
|
||||
linkedAccountAliases = accounts.stream().map(LinkedAccountRepresentation::getProviderAlias).toList();
|
||||
assertThat(linkedAccountAliases, contains("myoidc", "mysaml"));
|
||||
|
||||
// now use a search string to filter the results.
|
||||
accounts = linkedAccountsRep("linked=false&search=*o*");
|
||||
assertEquals(3, accounts.size());
|
||||
linkedAccountAliases = accounts.stream().map(LinkedAccountRepresentation::getProviderAlias).toList();
|
||||
assertThat(linkedAccountAliases, contains("facebook", "microsoft", "myoidc"));
|
||||
|
||||
// finally use the search string with pagination.
|
||||
accounts = linkedAccountsRep("linked=false&search=*o*&first=1&max=1");
|
||||
assertEquals(1, accounts.size());
|
||||
linkedAccountAliases = accounts.stream().map(LinkedAccountRepresentation::getProviderAlias).toList();
|
||||
assertThat(linkedAccountAliases, contains("microsoft"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveLinkedAccount() throws IOException {
|
||||
assertTrue(findLinkedAccount("github").isConnected());
|
||||
SimpleHttpDefault.doDelete(getAccountUrl("linked-accounts/github"), client).auth(tokenUtil.getToken()).acceptJson().asResponse();
|
||||
assertFalse(findLinkedAccount("github").isConnected());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue