Add enabled field to OrganizationEntity
Closes #28891 Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
This commit is contained in:
parent
4c6f3ce35d
commit
dae1eada3d
13 changed files with 171 additions and 39 deletions
|
@ -28,6 +28,7 @@ public class OrganizationRepresentation {
|
|||
|
||||
private String id;
|
||||
private String name;
|
||||
private boolean enabled = true;
|
||||
private Map<String, List<String>> attributes;
|
||||
private Set<OrganizationDomainRepresentation> domains;
|
||||
|
||||
|
@ -47,6 +48,14 @@ public class OrganizationRepresentation {
|
|||
return name;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
|
|
@ -19,10 +19,12 @@ package org.keycloak.models.cache.infinispan;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.cluster.ClusterProvider;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.CredentialValidationOutput;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.OrganizationModel;
|
||||
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
|
||||
import org.keycloak.common.constants.ServiceAccountConstants;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
|
@ -54,6 +56,7 @@ import org.keycloak.models.cache.infinispan.events.UserUpdatedEvent;
|
|||
import org.keycloak.models.cache.infinispan.stream.InIdentityProviderPredicate;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
|
||||
import org.keycloak.organization.OrganizationProvider;
|
||||
import org.keycloak.storage.CacheableStorageProviderModel;
|
||||
import org.keycloak.storage.DatastoreProvider;
|
||||
import org.keycloak.storage.StoreManagers;
|
||||
|
@ -336,6 +339,20 @@ public class UserCacheSession implements UserCache, OnCreateComponent, OnUpdateC
|
|||
protected UserModel cacheUser(RealmModel realm, UserModel delegate, Long revision) {
|
||||
int notBefore = getDelegate().getNotBeforeOfUser(realm, delegate);
|
||||
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.ORGANIZATION)) {
|
||||
// check if user is member of a disabled organization.
|
||||
OrganizationProvider organizationProvider = session.getProvider(OrganizationProvider.class);
|
||||
OrganizationModel organization = organizationProvider.getByMember(delegate);
|
||||
if (organization != null && organization.isManaged(delegate) && !organization.isEnabled()) {
|
||||
return new ReadOnlyUserModelDelegate(delegate) {
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
StorageId storageId = delegate.getFederationLink() != null ?
|
||||
new StorageId(delegate.getFederationLink(), delegate.getId()) : new StorageId(delegate.getId());
|
||||
CachedUser cached = null;
|
||||
|
|
|
@ -51,6 +51,9 @@ public class OrganizationEntity {
|
|||
@Column(name = "NAME")
|
||||
private String name;
|
||||
|
||||
@Column(name = "ENABLED")
|
||||
private boolean enabled;
|
||||
|
||||
@Column(name = "REALM_ID")
|
||||
private String realmId;
|
||||
|
||||
|
@ -72,6 +75,14 @@ public class OrganizationEntity {
|
|||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
|||
entity.setGroupId(group.getId());
|
||||
entity.setRealmId(realm.getId());
|
||||
entity.setName(name);
|
||||
entity.setEnabled(true);
|
||||
|
||||
em.persist(entity);
|
||||
|
||||
|
|
|
@ -76,6 +76,16 @@ public final class OrganizationAdapter implements OrganizationModel, JpaModel<Or
|
|||
return entity.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return entity.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
entity.setEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttributes(Map<String, List<String>> attributes) {
|
||||
if (attributes == null) {
|
||||
|
|
|
@ -83,6 +83,9 @@
|
|||
<column name="ID" type="VARCHAR(255)">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="ENABLED" type="BOOLEAN">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="REALM_ID" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.util.stream.Collectors;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.constants.ServiceAccountConstants;
|
||||
import org.keycloak.common.util.reflections.Types;
|
||||
import org.keycloak.component.ComponentFactory;
|
||||
|
@ -50,6 +51,7 @@ import org.keycloak.models.GroupModel;
|
|||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.OrganizationModel;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
@ -62,6 +64,7 @@ import org.keycloak.models.cache.OnUserCache;
|
|||
import org.keycloak.models.cache.UserCache;
|
||||
import org.keycloak.models.utils.ComponentUtil;
|
||||
import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
|
||||
import org.keycloak.organization.OrganizationProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
import org.keycloak.storage.datastore.DefaultDatastoreProvider;
|
||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||
|
@ -111,6 +114,20 @@ public class UserStorageManager extends AbstractStorageManager<UserStorageProvid
|
|||
* @return
|
||||
*/
|
||||
protected UserModel importValidation(RealmModel realm, UserModel user) {
|
||||
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.ORGANIZATION) && user != null) {
|
||||
// check if user belongs to a disabled organization
|
||||
OrganizationProvider organizationProvider = session.getProvider(OrganizationProvider.class);
|
||||
OrganizationModel organization = organizationProvider.getByMember(user);
|
||||
if (organization != null && organization.isManaged(user) && !organization.isEnabled()) {
|
||||
return new ReadOnlyUserModelDelegate(user) {
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
if (user == null || user.getFederationLink() == null) return user;
|
||||
|
||||
UserStorageProviderModel model = getStorageProviderModel(realm, user.getFederationLink());
|
||||
|
|
|
@ -34,6 +34,10 @@ public interface OrganizationModel {
|
|||
|
||||
String getName();
|
||||
|
||||
boolean isEnabled();
|
||||
|
||||
void setEnabled(boolean enabled);
|
||||
|
||||
Map<String, List<String>> getAttributes();
|
||||
|
||||
void setAttributes(Map<String, List<String>> attributes);
|
||||
|
|
|
@ -173,6 +173,7 @@ public class OrganizationResource {
|
|||
|
||||
rep.setId(model.getId());
|
||||
rep.setName(model.getName());
|
||||
rep.setEnabled(model.isEnabled());
|
||||
rep.setAttributes(model.getAttributes());
|
||||
model.getDomains().filter(Objects::nonNull).map(this::toRepresentation)
|
||||
.forEach(rep::addDomain);
|
||||
|
@ -193,6 +194,7 @@ public class OrganizationResource {
|
|||
}
|
||||
|
||||
model.setName(rep.getName());
|
||||
model.setEnabled(rep.isEnabled());
|
||||
model.setAttributes(rep.getAttributes());
|
||||
model.setDomains(Optional.ofNullable(rep.getDomains()).orElse(Set.of()).stream()
|
||||
.filter(Objects::nonNull)
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.testsuite.organization.admin;
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
@ -28,10 +29,12 @@ import jakarta.ws.rs.core.Response.Status;
|
|||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.keycloak.admin.client.resource.OrganizationResource;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.representations.idm.OrganizationDomainRepresentation;
|
||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.admin.AbstractAdminTest;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.admin.Users;
|
||||
|
@ -200,4 +203,43 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
|
|||
return actual;
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertBrokerRegistration(OrganizationResource organization, String email) {
|
||||
// login with email only
|
||||
oauth.clientId("broker-app");
|
||||
loginPage.open(bc.consumerRealmName());
|
||||
log.debug("Logging in");
|
||||
Assert.assertFalse(loginPage.isPasswordInputPresent());
|
||||
Assert.assertFalse(loginPage.isSocialButtonPresent(bc.getIDPAlias()));
|
||||
loginPage.loginUsername(email);
|
||||
|
||||
// user automatically redirected to the organization identity provider
|
||||
waitForPage(driver, "sign in to", true);
|
||||
Assert.assertTrue("Driver should be on the provider realm page right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
|
||||
// login to the organization identity provider and run the configured first broker login flow
|
||||
loginPage.login(email, bc.getUserPassword());
|
||||
waitForPage(driver, "update account information", false);
|
||||
updateAccountInformationPage.assertCurrent();
|
||||
Assert.assertTrue("We must be on correct realm right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
|
||||
log.debug("Updating info on updateAccount page");
|
||||
updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), email, "Firstname", "Lastname");
|
||||
|
||||
assertIsMember(email, organization);
|
||||
}
|
||||
|
||||
protected void assertIsMember(String userEmail, OrganizationResource organization) {
|
||||
UserRepresentation account = getUserRepresentation(userEmail);
|
||||
UserRepresentation member = organization.members().member(account.getId()).toRepresentation();
|
||||
Assert.assertEquals(account.getId(), member.getId());
|
||||
}
|
||||
|
||||
protected UserRepresentation getUserRepresentation(String userEmail) {
|
||||
UsersResource users = adminClient.realm(bc.consumerRealmName()).users();
|
||||
List<UserRepresentation> reps = users.searchByEmail(userEmail, true);
|
||||
Assert.assertFalse(reps.isEmpty());
|
||||
Assert.assertEquals(1, reps.size());
|
||||
return reps.get(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -582,37 +582,6 @@ public class OrganizationBrokerSelfRegistrationTest extends AbstractOrganization
|
|||
appPage.assertCurrent();
|
||||
}
|
||||
|
||||
private void assertBrokerRegistration(OrganizationResource organization, String email) {
|
||||
// login with email only
|
||||
oauth.clientId("broker-app");
|
||||
loginPage.open(bc.consumerRealmName());
|
||||
log.debug("Logging in");
|
||||
Assert.assertFalse(loginPage.isPasswordInputPresent());
|
||||
Assert.assertFalse(loginPage.isSocialButtonPresent(bc.getIDPAlias()));
|
||||
loginPage.loginUsername(email);
|
||||
|
||||
// user automatically redirected to the organization identity provider
|
||||
waitForPage(driver, "sign in to", true);
|
||||
Assert.assertTrue("Driver should be on the provider realm page right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
|
||||
// login to the organization identity provider and run the configured first broker login flow
|
||||
loginPage.login(email, bc.getUserPassword());
|
||||
waitForPage(driver, "update account information", false);
|
||||
updateAccountInformationPage.assertCurrent();
|
||||
Assert.assertTrue("We must be on correct realm right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
|
||||
log.debug("Updating info on updateAccount page");
|
||||
updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), email, "Firstname", "Lastname");
|
||||
|
||||
assertIsMember(email, organization);
|
||||
}
|
||||
|
||||
private void assertIsMember(String userEmail, OrganizationResource organization) {
|
||||
UserRepresentation account = getUserRepresentation(userEmail);
|
||||
UserRepresentation member = organization.members().member(account.getId()).toRepresentation();
|
||||
Assert.assertEquals(account.getId(), member.getId());
|
||||
}
|
||||
|
||||
private void assertIsNotMember(String userEmail, OrganizationResource organization) {
|
||||
UsersResource users = adminClient.realm(bc.consumerRealmName()).users();
|
||||
List<UserRepresentation> reps = users.searchByEmail(userEmail, true);
|
||||
|
@ -629,12 +598,4 @@ public class OrganizationBrokerSelfRegistrationTest extends AbstractOrganization
|
|||
} catch (NotFoundException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
private UserRepresentation getUserRepresentation(String userEmail) {
|
||||
UsersResource users = adminClient.realm(bc.consumerRealmName()).users();
|
||||
List<UserRepresentation> reps = users.searchByEmail(userEmail, true);
|
||||
Assert.assertFalse(reps.isEmpty());
|
||||
Assert.assertEquals(1, reps.size());
|
||||
return reps.get(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import static org.hamcrest.Matchers.empty;
|
|||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
@ -160,6 +161,55 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
|||
assertEquals(expectedRep.getEmail(), existingRep.getEmail());
|
||||
assertEquals(expectedRep.getFirstName(), existingRep.getFirstName());
|
||||
assertEquals(expectedRep.getLastName(), existingRep.getLastName());
|
||||
assertTrue(expectedRep.isEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllDisabledOrganization() {
|
||||
OrganizationRepresentation orgRep = createOrganization();
|
||||
OrganizationResource organization = testRealm().organizations().get(orgRep.getId());
|
||||
|
||||
// add some unmanaged members to the organization.
|
||||
for (int i = 0; i < 5; i++) {
|
||||
addMember(organization, "member-" + i + "@neworg.org");
|
||||
}
|
||||
|
||||
// onboard a test user by authenticating using the organization's provider.
|
||||
super.assertBrokerRegistration(organization, bc.getUserEmail());
|
||||
|
||||
// disable the organization and check that fetching its representation has it disabled.
|
||||
orgRep.setEnabled(false);
|
||||
try (Response response = organization.update(orgRep)) {
|
||||
assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
|
||||
}
|
||||
OrganizationRepresentation existingOrg = organization.toRepresentation();
|
||||
assertThat(orgRep.getId(), is(equalTo(existingOrg.getId())));
|
||||
assertThat(orgRep.getName(), is(equalTo(existingOrg.getName())));
|
||||
assertThat(existingOrg.isEnabled(), is(false));
|
||||
|
||||
// now fetch all users from the org - unmanaged users should still be enabled, but managed ones should not.
|
||||
List<UserRepresentation> existing = organization.members().getAll();;
|
||||
assertThat(existing, not(empty()));
|
||||
assertThat(existing, hasSize(6));
|
||||
for (UserRepresentation user : existing) {
|
||||
if (user.getEmail().equals(bc.getUserEmail())) {
|
||||
assertThat(user.isEnabled(), is(false));
|
||||
} else {
|
||||
assertThat(user.isEnabled(), is(true));
|
||||
}
|
||||
}
|
||||
|
||||
// fetching users from the users endpoint should have the same result.
|
||||
existing = testRealm().users().search("*neworg*",0, 10);
|
||||
assertThat(existing, not(empty()));
|
||||
assertThat(existing, hasSize(6));
|
||||
for (UserRepresentation user : existing) {
|
||||
if (user.getEmail().equals(bc.getUserEmail())) {
|
||||
assertThat(user.isEnabled(), is(false));
|
||||
} else {
|
||||
assertThat(user.isEnabled(), is(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ public class OrganizationTest extends AbstractOrganizationTest {
|
|||
|
||||
assertEquals(organizationName, expected.getName());
|
||||
expected.setName("acme");
|
||||
expected.setEnabled(false);
|
||||
|
||||
OrganizationResource organization = testRealm().organizations().get(expected.getId());
|
||||
|
||||
|
@ -66,6 +67,7 @@ public class OrganizationTest extends AbstractOrganizationTest {
|
|||
assertEquals(expected.getId(), existing.getId());
|
||||
assertEquals(expected.getName(), existing.getName());
|
||||
assertEquals(1, existing.getDomains().size());
|
||||
assertThat(existing.isEnabled(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -75,6 +77,7 @@ public class OrganizationTest extends AbstractOrganizationTest {
|
|||
assertNotNull(existing);
|
||||
assertEquals(expected.getId(), existing.getId());
|
||||
assertEquals(expected.getName(), existing.getName());
|
||||
assertThat(expected.isEnabled(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -103,6 +106,7 @@ public class OrganizationTest extends AbstractOrganizationTest {
|
|||
assertThat(existing, hasSize(1));
|
||||
OrganizationRepresentation orgRep = existing.get(0);
|
||||
assertThat(orgRep.getName(), is(equalTo("wayne-industries")));
|
||||
assertThat(orgRep.isEnabled(), is(true));
|
||||
assertThat(orgRep.getDomains(), hasSize(2));
|
||||
assertThat(orgRep.getDomain("wayneind.com"), not(nullValue()));
|
||||
assertThat(orgRep.getDomain("wayneind-gotham.com"), not(nullValue()));
|
||||
|
@ -111,6 +115,7 @@ public class OrganizationTest extends AbstractOrganizationTest {
|
|||
assertThat(existing, hasSize(1));
|
||||
orgRep = existing.get(0);
|
||||
assertThat(orgRep.getName(), is(equalTo("Gotham-Bank")));
|
||||
assertThat(orgRep.isEnabled(), is(true));
|
||||
assertThat(orgRep.getDomains(), hasSize(2));
|
||||
assertThat(orgRep.getDomain("gtbank.com"), not(nullValue()));
|
||||
assertThat(orgRep.getDomain("gtbank.net"), not(nullValue()));
|
||||
|
|
Loading…
Reference in a new issue