Add organizations enabled/disabled capability
Closes #28804 Signed-off-by: vramik <vramik@redhat.com>
This commit is contained in:
parent
80de3a0a71
commit
278341aff9
34 changed files with 331 additions and 64 deletions
|
@ -214,6 +214,8 @@ public class RealmRepresentation {
|
||||||
|
|
||||||
protected Boolean userManagedAccessAllowed;
|
protected Boolean userManagedAccessAllowed;
|
||||||
|
|
||||||
|
protected Boolean organizationsEnabled;
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected Boolean social;
|
protected Boolean social;
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
@ -1420,6 +1422,14 @@ public class RealmRepresentation {
|
||||||
return userManagedAccessAllowed;
|
return userManagedAccessAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean isOrganizationsEnabled() {
|
||||||
|
return organizationsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrganizationsEnabled(Boolean organizationsEnabled) {
|
||||||
|
this.organizationsEnabled = organizationsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public Map<String, String> getAttributesOrEmpty() {
|
public Map<String, String> getAttributesOrEmpty() {
|
||||||
return (Map<String, String>) (attributes == null ? Collections.emptyMap() : attributes);
|
return (Map<String, String>) (attributes == null ? Collections.emptyMap() : attributes);
|
||||||
|
|
|
@ -3132,3 +3132,5 @@ searchClientRegistration=Search for policy
|
||||||
importFileHelp=File to import a key
|
importFileHelp=File to import a key
|
||||||
logo=Logo
|
logo=Logo
|
||||||
avatarImage=Avatar image
|
avatarImage=Avatar image
|
||||||
|
organizationsEnabled=Organizations
|
||||||
|
organizationsEnabledHelp=If enabled, allows managing organizations. Otherwise, existing organizations are still kept but you will not be able to manage them anymore or authenticate their members.
|
||||||
|
|
|
@ -36,6 +36,8 @@ import {
|
||||||
import { useFetch } from "../utils/useFetch";
|
import { useFetch } from "../utils/useFetch";
|
||||||
import { UIRealmRepresentation } from "./RealmSettingsTabs";
|
import { UIRealmRepresentation } from "./RealmSettingsTabs";
|
||||||
|
|
||||||
|
import useIsFeatureEnabled, { Feature } from "../utils/useIsFeatureEnabled";
|
||||||
|
|
||||||
type RealmSettingsGeneralTabProps = {
|
type RealmSettingsGeneralTabProps = {
|
||||||
realm: UIRealmRepresentation;
|
realm: UIRealmRepresentation;
|
||||||
save: (realm: UIRealmRepresentation) => void;
|
save: (realm: UIRealmRepresentation) => void;
|
||||||
|
@ -105,6 +107,8 @@ function RealmSettingsGeneralTabForm({
|
||||||
setValue,
|
setValue,
|
||||||
formState: { isDirty, errors },
|
formState: { isDirty, errors },
|
||||||
} = form;
|
} = form;
|
||||||
|
const isFeatureEnabled = useIsFeatureEnabled();
|
||||||
|
const isOrganizationsEnabled = isFeatureEnabled(Feature.Organizations);
|
||||||
|
|
||||||
const setupForm = () => {
|
const setupForm = () => {
|
||||||
convertToFormValues(realm, setValue);
|
convertToFormValues(realm, setValue);
|
||||||
|
@ -212,6 +216,13 @@ function RealmSettingsGeneralTabForm({
|
||||||
label={t("userManagedAccess")}
|
label={t("userManagedAccess")}
|
||||||
labelIcon={t("userManagedAccessHelp")}
|
labelIcon={t("userManagedAccessHelp")}
|
||||||
/>
|
/>
|
||||||
|
{isOrganizationsEnabled && (
|
||||||
|
<DefaultSwitchControl
|
||||||
|
name="organizationsEnabled"
|
||||||
|
label={t("organizationsEnabled")}
|
||||||
|
labelIcon={t("organizationsEnabledHelp")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<SelectControl
|
<SelectControl
|
||||||
name="unmanagedAttributePolicy"
|
name="unmanagedAttributePolicy"
|
||||||
label={t("unmanagedAttributes")}
|
label={t("unmanagedAttributes")}
|
||||||
|
|
|
@ -10,6 +10,7 @@ export enum Feature {
|
||||||
TransientUsers = "TRANSIENT_USERS",
|
TransientUsers = "TRANSIENT_USERS",
|
||||||
ClientTypes = "CLIENT_TYPES",
|
ClientTypes = "CLIENT_TYPES",
|
||||||
DeclarativeUI = "DECLARATIVE_UI",
|
DeclarativeUI = "DECLARATIVE_UI",
|
||||||
|
Organizations = "ORGANIZATION",
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function useIsFeatureEnabled() {
|
export default function useIsFeatureEnabled() {
|
||||||
|
|
|
@ -81,6 +81,7 @@ export default interface RealmRepresentation {
|
||||||
offlineSessionIdleTimeout?: number;
|
offlineSessionIdleTimeout?: number;
|
||||||
offlineSessionMaxLifespan?: number;
|
offlineSessionMaxLifespan?: number;
|
||||||
offlineSessionMaxLifespanEnabled?: boolean;
|
offlineSessionMaxLifespanEnabled?: boolean;
|
||||||
|
organizationsEnabled?: boolean;
|
||||||
otpPolicyAlgorithm?: string;
|
otpPolicyAlgorithm?: string;
|
||||||
otpPolicyDigits?: number;
|
otpPolicyDigits?: number;
|
||||||
otpPolicyInitialCounter?: number;
|
otpPolicyInitialCounter?: number;
|
||||||
|
|
|
@ -32,6 +32,8 @@ import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -860,19 +862,34 @@ public class RealmAdapter implements CachedRealmModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<IdentityProviderModel> getIdentityProvidersStream() {
|
public Stream<IdentityProviderModel> getIdentityProvidersStream() {
|
||||||
if (isUpdated()) return updated.getIdentityProvidersStream();
|
if (isUpdated()) return updated.getIdentityProvidersStream().map(this::createOrganizationAwareIdentityProviderModel);
|
||||||
return cached.getIdentityProviders().stream();
|
return cached.getIdentityProviders().stream().map(this::createOrganizationAwareIdentityProviderModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdentityProviderModel getIdentityProviderByAlias(String alias) {
|
public IdentityProviderModel getIdentityProviderByAlias(String alias) {
|
||||||
if (isUpdated()) return updated.getIdentityProviderByAlias(alias);
|
if (isUpdated()) return createOrganizationAwareIdentityProviderModel(updated.getIdentityProviderByAlias(alias));
|
||||||
return getIdentityProvidersStream()
|
return getIdentityProvidersStream()
|
||||||
.filter(model -> Objects.equals(model.getAlias(), alias))
|
.filter(model -> Objects.equals(model.getAlias(), alias))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
|
.map(this::createOrganizationAwareIdentityProviderModel)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IdentityProviderModel createOrganizationAwareIdentityProviderModel(IdentityProviderModel idp) {
|
||||||
|
if (!Profile.isFeatureEnabled(Profile.Feature.ORGANIZATION)) return idp;
|
||||||
|
return new IdentityProviderModel(idp) {
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
// if IdP is bound to an org
|
||||||
|
if (getOrganizationId() != null) {
|
||||||
|
return session.getProvider(OrganizationProvider.class).isEnabled() && super.isEnabled();
|
||||||
|
}
|
||||||
|
return super.isEnabled();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addIdentityProvider(IdentityProviderModel identityProvider) {
|
public void addIdentityProvider(IdentityProviderModel identityProvider) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
|
@ -1748,4 +1765,21 @@ public class RealmAdapter implements CachedRealmModel {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("%s@%08x", getId(), hashCode());
|
return String.format("%s@%08x", getId(), hashCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOrganizationsEnabled() {
|
||||||
|
if (isUpdated()) return featureAwareIsOrganizationsEnabled(updated.isOrganizationsEnabled());
|
||||||
|
return featureAwareIsOrganizationsEnabled(cached.isOrganizationsEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOrganizationsEnabled(boolean organizationsEnabled) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setOrganizationsEnabled(organizationsEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean featureAwareIsOrganizationsEnabled(boolean isOrganizationsEnabled) {
|
||||||
|
if (!Profile.isFeatureEnabled(Profile.Feature.ORGANIZATION)) return false;
|
||||||
|
return isOrganizationsEnabled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -340,10 +340,12 @@ public class UserCacheSession implements UserCache, OnCreateComponent, OnUpdateC
|
||||||
int notBefore = getDelegate().getNotBeforeOfUser(realm, delegate);
|
int notBefore = getDelegate().getNotBeforeOfUser(realm, delegate);
|
||||||
|
|
||||||
if (Profile.isFeatureEnabled(Profile.Feature.ORGANIZATION)) {
|
if (Profile.isFeatureEnabled(Profile.Feature.ORGANIZATION)) {
|
||||||
// check if user is member of a disabled organization.
|
// check if provider is enabled and user is managed member of a disabled organization OR provider is disabled and user is managed member
|
||||||
OrganizationProvider organizationProvider = session.getProvider(OrganizationProvider.class);
|
OrganizationProvider organizationProvider = session.getProvider(OrganizationProvider.class);
|
||||||
OrganizationModel organization = organizationProvider.getByMember(delegate);
|
OrganizationModel organization = organizationProvider.getByMember(delegate);
|
||||||
if (organization != null && organization.isManaged(delegate) && !organization.isEnabled()) {
|
|
||||||
|
if ((organizationProvider.isEnabled() && organization != null && organization.isManaged(delegate) && !organization.isEnabled()) ||
|
||||||
|
(!organizationProvider.isEnabled() && organization != null && organization.isManaged(delegate))) {
|
||||||
return new ReadOnlyUserModelDelegate(delegate) {
|
return new ReadOnlyUserModelDelegate(delegate) {
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
|
|
|
@ -70,6 +70,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
||||||
protected boolean resetPasswordAllowed;
|
protected boolean resetPasswordAllowed;
|
||||||
protected boolean identityFederationEnabled;
|
protected boolean identityFederationEnabled;
|
||||||
protected boolean editUsernameAllowed;
|
protected boolean editUsernameAllowed;
|
||||||
|
protected boolean organizationsEnabled;
|
||||||
//--- brute force settings
|
//--- brute force settings
|
||||||
protected boolean bruteForceProtected;
|
protected boolean bruteForceProtected;
|
||||||
protected boolean permanentLockout;
|
protected boolean permanentLockout;
|
||||||
|
@ -191,6 +192,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
||||||
resetPasswordAllowed = model.isResetPasswordAllowed();
|
resetPasswordAllowed = model.isResetPasswordAllowed();
|
||||||
identityFederationEnabled = model.isIdentityFederationEnabled();
|
identityFederationEnabled = model.isIdentityFederationEnabled();
|
||||||
editUsernameAllowed = model.isEditUsernameAllowed();
|
editUsernameAllowed = model.isEditUsernameAllowed();
|
||||||
|
organizationsEnabled = model.isOrganizationsEnabled();
|
||||||
//--- brute force settings
|
//--- brute force settings
|
||||||
bruteForceProtected = model.isBruteForceProtected();
|
bruteForceProtected = model.isBruteForceProtected();
|
||||||
permanentLockout = model.isPermanentLockout();
|
permanentLockout = model.isPermanentLockout();
|
||||||
|
@ -423,6 +425,10 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
||||||
return editUsernameAllowed;
|
return editUsernameAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isOrganizationsEnabled() {
|
||||||
|
return organizationsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
public String getDefaultSignatureAlgorithm() {
|
public String getDefaultSignatureAlgorithm() {
|
||||||
return defaultSignatureAlgorithm;
|
return defaultSignatureAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1173,6 +1173,16 @@ public class RealmAdapter implements StorageProviderRealmModel, JpaModel<RealmEn
|
||||||
em.flush();
|
em.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOrganizationsEnabled() {
|
||||||
|
return getAttribute(RealmAttributes.ORGANIZATIONS_ENABLED, Boolean.FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOrganizationsEnabled(boolean organizationsEnabled) {
|
||||||
|
setAttribute(RealmAttributes.ORGANIZATIONS_ENABLED, organizationsEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientModel getMasterAdminClient() {
|
public ClientModel getMasterAdminClient() {
|
||||||
String masterAdminClientId = realm.getMasterAdminClient();
|
String masterAdminClientId = realm.getMasterAdminClient();
|
||||||
|
|
|
@ -56,4 +56,5 @@ public interface RealmAttributes {
|
||||||
|
|
||||||
String FIRST_BROKER_LOGIN_FLOW_ID = "firstBrokerLoginFlowId";
|
String FIRST_BROKER_LOGIN_FLOW_ID = "firstBrokerLoginFlowId";
|
||||||
|
|
||||||
|
String ORGANIZATIONS_ENABLED = "organizationsEnabled";
|
||||||
}
|
}
|
||||||
|
|
|
@ -398,7 +398,7 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return getAllStream().findAny().isPresent();
|
return realm.isOrganizationsEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -91,7 +91,7 @@ public final class OrganizationAdapter implements OrganizationModel, JpaModel<Or
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return entity.isEnabled();
|
return provider.isEnabled() && entity.isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -136,8 +136,6 @@ public final class OrganizationAdapter implements OrganizationModel, JpaModel<Or
|
||||||
throw new ModelValidationException("You must provide at least one domain");
|
throw new ModelValidationException("You must provide at least one domain");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<IdentityProviderModel> idps = this.getIdentityProviders().toList();
|
|
||||||
|
|
||||||
Map<String, OrganizationDomainModel> modelMap = domains.stream()
|
Map<String, OrganizationDomainModel> modelMap = domains.stream()
|
||||||
.map(this::validateDomain)
|
.map(this::validateDomain)
|
||||||
.collect(Collectors.toMap(OrganizationDomainModel::getName, Function.identity()));
|
.collect(Collectors.toMap(OrganizationDomainModel::getName, Function.identity()));
|
||||||
|
@ -147,17 +145,16 @@ public final class OrganizationAdapter implements OrganizationModel, JpaModel<Or
|
||||||
if (modelMap.containsKey(domainEntity.getName())) {
|
if (modelMap.containsKey(domainEntity.getName())) {
|
||||||
domainEntity.setVerified(modelMap.get(domainEntity.getName()).getVerified());
|
domainEntity.setVerified(modelMap.get(domainEntity.getName()).getVerified());
|
||||||
modelMap.remove(domainEntity.getName());
|
modelMap.remove(domainEntity.getName());
|
||||||
}
|
} else {
|
||||||
// remove domain that is not found in the new set.
|
// remove domain that is not found in the new set.
|
||||||
else {
|
|
||||||
this.entity.removeDomain(domainEntity);
|
this.entity.removeDomain(domainEntity);
|
||||||
// check if any idp is assigned to the removed domain, and unset the domain if that's the case.
|
// check if any idp is assigned to the removed domain, and unset the domain if that's the case.
|
||||||
idps.forEach(idp -> {
|
getIdentityProviders()
|
||||||
if (Objects.equals(domainEntity.getName(), idp.getConfig().get(ORGANIZATION_DOMAIN_ATTRIBUTE))) {
|
.filter(idp -> Objects.equals(domainEntity.getName(), idp.getConfig().get(ORGANIZATION_DOMAIN_ATTRIBUTE)))
|
||||||
idp.getConfig().remove(ORGANIZATION_DOMAIN_ATTRIBUTE);
|
.forEach(idp -> {
|
||||||
realm.updateIdentityProvider(idp);
|
idp.getConfig().remove(ORGANIZATION_DOMAIN_ATTRIBUTE);
|
||||||
}
|
realm.updateIdentityProvider(idp);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,6 @@ import org.keycloak.organization.OrganizationProvider;
|
||||||
import org.keycloak.storage.client.ClientStorageProvider;
|
import org.keycloak.storage.client.ClientStorageProvider;
|
||||||
import org.keycloak.storage.datastore.DefaultDatastoreProvider;
|
import org.keycloak.storage.datastore.DefaultDatastoreProvider;
|
||||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||||
import org.keycloak.storage.federated.UserGroupMembershipFederatedStorage;
|
|
||||||
import org.keycloak.storage.managers.UserStorageSyncManager;
|
import org.keycloak.storage.managers.UserStorageSyncManager;
|
||||||
import org.keycloak.storage.user.ImportedUserValidation;
|
import org.keycloak.storage.user.ImportedUserValidation;
|
||||||
import org.keycloak.storage.user.UserBulkUpdateProvider;
|
import org.keycloak.storage.user.UserBulkUpdateProvider;
|
||||||
|
@ -116,10 +115,12 @@ public class UserStorageManager extends AbstractStorageManager<UserStorageProvid
|
||||||
protected UserModel importValidation(RealmModel realm, UserModel user) {
|
protected UserModel importValidation(RealmModel realm, UserModel user) {
|
||||||
|
|
||||||
if (Profile.isFeatureEnabled(Profile.Feature.ORGANIZATION) && user != null) {
|
if (Profile.isFeatureEnabled(Profile.Feature.ORGANIZATION) && user != null) {
|
||||||
// check if user belongs to a disabled organization
|
// check if provider is enabled and user is managed member of a disabled organization OR provider is disabled and user is managed member
|
||||||
OrganizationProvider organizationProvider = session.getProvider(OrganizationProvider.class);
|
OrganizationProvider organizationProvider = session.getProvider(OrganizationProvider.class);
|
||||||
OrganizationModel organization = organizationProvider.getByMember(user);
|
OrganizationModel organization = organizationProvider.getByMember(user);
|
||||||
if (organization != null && organization.isManaged(user) && !organization.isEnabled()) {
|
|
||||||
|
if ((organizationProvider.isEnabled() && organization != null && organization.isManaged(user) && !organization.isEnabled()) ||
|
||||||
|
(!organizationProvider.isEnabled() && organization != null && organization.isManaged(user))) {
|
||||||
return new ReadOnlyUserModelDelegate(user) {
|
return new ReadOnlyUserModelDelegate(user) {
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
|
@ -128,6 +129,7 @@ public class UserStorageManager extends AbstractStorageManager<UserStorageProvid
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user == null || user.getFederationLink() == null) return user;
|
if (user == null || user.getFederationLink() == null) return user;
|
||||||
|
|
||||||
UserStorageProviderModel model = getStorageProviderModel(realm, user.getFederationLink());
|
UserStorageProviderModel model = getStorageProviderModel(realm, user.getFederationLink());
|
||||||
|
|
|
@ -275,6 +275,7 @@ public class DefaultExportImportManager implements ExportImportManager {
|
||||||
if (rep.isDuplicateEmailsAllowed() != null) newRealm.setDuplicateEmailsAllowed(rep.isDuplicateEmailsAllowed());
|
if (rep.isDuplicateEmailsAllowed() != null) newRealm.setDuplicateEmailsAllowed(rep.isDuplicateEmailsAllowed());
|
||||||
if (rep.isResetPasswordAllowed() != null) newRealm.setResetPasswordAllowed(rep.isResetPasswordAllowed());
|
if (rep.isResetPasswordAllowed() != null) newRealm.setResetPasswordAllowed(rep.isResetPasswordAllowed());
|
||||||
if (rep.isEditUsernameAllowed() != null) newRealm.setEditUsernameAllowed(rep.isEditUsernameAllowed());
|
if (rep.isEditUsernameAllowed() != null) newRealm.setEditUsernameAllowed(rep.isEditUsernameAllowed());
|
||||||
|
if (rep.isOrganizationsEnabled() != null) newRealm.setOrganizationsEnabled(rep.isOrganizationsEnabled());
|
||||||
if (rep.getLoginTheme() != null) newRealm.setLoginTheme(rep.getLoginTheme());
|
if (rep.getLoginTheme() != null) newRealm.setLoginTheme(rep.getLoginTheme());
|
||||||
if (rep.getAccountTheme() != null) newRealm.setAccountTheme(rep.getAccountTheme());
|
if (rep.getAccountTheme() != null) newRealm.setAccountTheme(rep.getAccountTheme());
|
||||||
if (rep.getAdminTheme() != null) newRealm.setAdminTheme(rep.getAdminTheme());
|
if (rep.getAdminTheme() != null) newRealm.setAdminTheme(rep.getAdminTheme());
|
||||||
|
@ -754,6 +755,7 @@ public class DefaultExportImportManager implements ExportImportManager {
|
||||||
if (rep.isDuplicateEmailsAllowed() != null) realm.setDuplicateEmailsAllowed(rep.isDuplicateEmailsAllowed());
|
if (rep.isDuplicateEmailsAllowed() != null) realm.setDuplicateEmailsAllowed(rep.isDuplicateEmailsAllowed());
|
||||||
if (rep.isResetPasswordAllowed() != null) realm.setResetPasswordAllowed(rep.isResetPasswordAllowed());
|
if (rep.isResetPasswordAllowed() != null) realm.setResetPasswordAllowed(rep.isResetPasswordAllowed());
|
||||||
if (rep.isEditUsernameAllowed() != null) realm.setEditUsernameAllowed(rep.isEditUsernameAllowed());
|
if (rep.isEditUsernameAllowed() != null) realm.setEditUsernameAllowed(rep.isEditUsernameAllowed());
|
||||||
|
if (rep.isOrganizationsEnabled() != null) realm.setOrganizationsEnabled(rep.isOrganizationsEnabled());
|
||||||
if (rep.getSslRequired() != null) realm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase()));
|
if (rep.getSslRequired() != null) realm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase()));
|
||||||
if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
|
if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
|
||||||
if (rep.getAccessCodeLifespanUserAction() != null)
|
if (rep.getAccessCodeLifespanUserAction() != null)
|
||||||
|
|
|
@ -387,6 +387,7 @@ public class ModelToRepresentation {
|
||||||
rep.setDuplicateEmailsAllowed(realm.isDuplicateEmailsAllowed());
|
rep.setDuplicateEmailsAllowed(realm.isDuplicateEmailsAllowed());
|
||||||
rep.setResetPasswordAllowed(realm.isResetPasswordAllowed());
|
rep.setResetPasswordAllowed(realm.isResetPasswordAllowed());
|
||||||
rep.setEditUsernameAllowed(realm.isEditUsernameAllowed());
|
rep.setEditUsernameAllowed(realm.isEditUsernameAllowed());
|
||||||
|
rep.setOrganizationsEnabled(realm.isOrganizationsEnabled());
|
||||||
rep.setDefaultSignatureAlgorithm(realm.getDefaultSignatureAlgorithm());
|
rep.setDefaultSignatureAlgorithm(realm.getDefaultSignatureAlgorithm());
|
||||||
rep.setRevokeRefreshToken(realm.isRevokeRefreshToken());
|
rep.setRevokeRefreshToken(realm.isRevokeRefreshToken());
|
||||||
rep.setRefreshTokenMaxReuse(realm.getRefreshTokenMaxReuse());
|
rep.setRefreshTokenMaxReuse(realm.getRefreshTokenMaxReuse());
|
||||||
|
|
|
@ -1120,4 +1120,13 @@ public class RealmModelDelegate implements RealmModel {
|
||||||
return delegate.searchForRolesStream(search, first, max);
|
return delegate.searchForRolesStream(search, first, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOrganizationsEnabled() {
|
||||||
|
return delegate.isOrganizationsEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOrganizationsEnabled(boolean organizationsEnabled) {
|
||||||
|
delegate.setOrganizationsEnabled(organizationsEnabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.organization;
|
package org.keycloak.organization;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
|
@ -1773,6 +1773,14 @@ public class IdentityBrokerStateTestHelpers {
|
||||||
public void decreaseRemainingCount(ClientInitialAccessModel clientInitialAccess) {
|
public void decreaseRemainingCount(ClientInitialAccessModel clientInitialAccess) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOrganizationsEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOrganizationsEnabled(boolean organizationsEnabled) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.keycloak.common.Profile.Feature;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>A model type representing the configuration for identity providers. It provides some common properties and also a {@link org.keycloak.models.IdentityProviderModel#config}
|
* <p>A model type representing the configuration for identity providers. It provides some common properties and also a {@link org.keycloak.models.IdentityProviderModel#config}
|
||||||
|
@ -319,4 +320,20 @@ public class IdentityProviderModel implements Serializable {
|
||||||
public void setMetadataDescriptorUrl(String metadataDescriptorUrl) {
|
public void setMetadataDescriptorUrl(String metadataDescriptorUrl) {
|
||||||
getConfig().put(METADATA_DESCRIPTOR_URL, metadataDescriptorUrl);
|
getConfig().put(METADATA_DESCRIPTOR_URL, metadataDescriptorUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hash = 5;
|
||||||
|
hash = 61 * hash + Objects.hashCode(this.internalId);
|
||||||
|
hash = 61 * hash + Objects.hashCode(this.alias);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) return true;
|
||||||
|
if (!(obj instanceof IdentityProviderModel)) return false;
|
||||||
|
return Objects.equals(getInternalId(), ((IdentityProviderModel) obj).getInternalId()) &&
|
||||||
|
Objects.equals(getAlias(), ((IdentityProviderModel) obj).getAlias());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,10 @@ public interface RealmModel extends RoleContainerModel {
|
||||||
|
|
||||||
void setUserManagedAccessAllowed(boolean userManagedAccessAllowed);
|
void setUserManagedAccessAllowed(boolean userManagedAccessAllowed);
|
||||||
|
|
||||||
|
boolean isOrganizationsEnabled();
|
||||||
|
|
||||||
|
void setOrganizationsEnabled(boolean organizationsEnabled);
|
||||||
|
|
||||||
void setAttribute(String name, String value);
|
void setAttribute(String name, String value);
|
||||||
default void setAttribute(String name, Boolean value) {
|
default void setAttribute(String name, Boolean value) {
|
||||||
setAttribute(name, value.toString());
|
setAttribute(name, value.toString());
|
||||||
|
|
|
@ -89,6 +89,7 @@ public class OrganizationResource {
|
||||||
@Operation( summary = "Creates a new organization")
|
@Operation( summary = "Creates a new organization")
|
||||||
public Response create(OrganizationRepresentation organization) {
|
public Response create(OrganizationRepresentation organization) {
|
||||||
auth.realm().requireManageRealm();
|
auth.realm().requireManageRealm();
|
||||||
|
checkOrganizationsEnabled();
|
||||||
if (organization == null) {
|
if (organization == null) {
|
||||||
throw ErrorResponse.error("Organization cannot be null.", Response.Status.BAD_REQUEST);
|
throw ErrorResponse.error("Organization cannot be null.", Response.Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
@ -126,6 +127,7 @@ public class OrganizationResource {
|
||||||
@Parameter(description = "The maximum number of results to be returned - defaults to 10") @QueryParam("max") @DefaultValue("10") Integer max
|
@Parameter(description = "The maximum number of results to be returned - defaults to 10") @QueryParam("max") @DefaultValue("10") Integer max
|
||||||
) {
|
) {
|
||||||
auth.realm().requireManageRealm();
|
auth.realm().requireManageRealm();
|
||||||
|
checkOrganizationsEnabled();
|
||||||
|
|
||||||
// check if are searching orgs by attribute.
|
// check if are searching orgs by attribute.
|
||||||
if(StringUtil.isNotBlank(searchQuery)) {
|
if(StringUtil.isNotBlank(searchQuery)) {
|
||||||
|
@ -150,6 +152,7 @@ public class OrganizationResource {
|
||||||
@Operation(summary = "Returns the organization associated with the specified id, or null if no organization is found")
|
@Operation(summary = "Returns the organization associated with the specified id, or null if no organization is found")
|
||||||
public OrganizationRepresentation get(@PathParam("id") String id) {
|
public OrganizationRepresentation get(@PathParam("id") String id) {
|
||||||
auth.realm().requireManageRealm();
|
auth.realm().requireManageRealm();
|
||||||
|
checkOrganizationsEnabled();
|
||||||
if (StringUtil.isBlank(id)) {
|
if (StringUtil.isBlank(id)) {
|
||||||
throw ErrorResponse.error("Id cannot be null.", Response.Status.BAD_REQUEST);
|
throw ErrorResponse.error("Id cannot be null.", Response.Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
@ -163,6 +166,7 @@ public class OrganizationResource {
|
||||||
@Operation(summary = "Deletes the organization with the specified id")
|
@Operation(summary = "Deletes the organization with the specified id")
|
||||||
public Response delete(@PathParam("id") String id) {
|
public Response delete(@PathParam("id") String id) {
|
||||||
auth.realm().requireManageRealm();
|
auth.realm().requireManageRealm();
|
||||||
|
checkOrganizationsEnabled();
|
||||||
if (StringUtil.isBlank(id)) {
|
if (StringUtil.isBlank(id)) {
|
||||||
throw ErrorResponse.error("Id cannot be null.", Response.Status.BAD_REQUEST);
|
throw ErrorResponse.error("Id cannot be null.", Response.Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
@ -179,6 +183,7 @@ public class OrganizationResource {
|
||||||
@Operation(summary = "Updates the organization with the specified id")
|
@Operation(summary = "Updates the organization with the specified id")
|
||||||
public Response update(@PathParam("id") String id, OrganizationRepresentation organization) {
|
public Response update(@PathParam("id") String id, OrganizationRepresentation organization) {
|
||||||
auth.realm().requireManageRealm();
|
auth.realm().requireManageRealm();
|
||||||
|
checkOrganizationsEnabled();
|
||||||
OrganizationModel model = getOrganization(id);
|
OrganizationModel model = getOrganization(id);
|
||||||
toModel(organization, model);
|
toModel(organization, model);
|
||||||
|
|
||||||
|
@ -187,11 +192,13 @@ public class OrganizationResource {
|
||||||
|
|
||||||
@Path("{id}/members")
|
@Path("{id}/members")
|
||||||
public OrganizationMemberResource members(@PathParam("id") String id) {
|
public OrganizationMemberResource members(@PathParam("id") String id) {
|
||||||
|
checkOrganizationsEnabled();
|
||||||
return new OrganizationMemberResource(session, getOrganization(id), auth, adminEvent);
|
return new OrganizationMemberResource(session, getOrganization(id), auth, adminEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("{id}/identity-providers")
|
@Path("{id}/identity-providers")
|
||||||
public OrganizationIdentityProvidersResource identityProvider(@PathParam("id") String id) {
|
public OrganizationIdentityProvidersResource identityProvider(@PathParam("id") String id) {
|
||||||
|
checkOrganizationsEnabled();
|
||||||
return new OrganizationIdentityProvidersResource(session, getOrganization(id), auth, adminEvent);
|
return new OrganizationIdentityProvidersResource(session, getOrganization(id), auth, adminEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,4 +266,10 @@ public class OrganizationResource {
|
||||||
private OrganizationDomainModel toModel(OrganizationDomainRepresentation domainRepresentation) {
|
private OrganizationDomainModel toModel(OrganizationDomainRepresentation domainRepresentation) {
|
||||||
return new OrganizationDomainModel(domainRepresentation.getName(), domainRepresentation.isVerified());
|
return new OrganizationDomainModel(domainRepresentation.getName(), domainRepresentation.isVerified());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkOrganizationsEnabled() {
|
||||||
|
if (provider != null && !provider.isEnabled()) {
|
||||||
|
throw ErrorResponse.error("Organizations not enabled for this realm.", Response.Status.NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
package org.keycloak.organization.authentication.authenticators.broker;
|
package org.keycloak.organization.authentication.authenticators.broker;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.keycloak.authentication.AuthenticationFlowContext;
|
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||||
|
@ -32,6 +31,8 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.organization.OrganizationProvider;
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
|
|
||||||
|
import static org.keycloak.organization.utils.Organizations.isEnabledAndOrganizationsPresent;
|
||||||
|
|
||||||
public class IdpAddOrganizationMemberAuthenticator extends AbstractIdpAuthenticator {
|
public class IdpAddOrganizationMemberAuthenticator extends AbstractIdpAuthenticator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -70,13 +71,13 @@ public class IdpAddOrganizationMemberAuthenticator extends AbstractIdpAuthentica
|
||||||
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
|
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||||
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
|
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
|
||||||
|
|
||||||
if (!provider.isEnabled()) {
|
if (!isEnabledAndOrganizationsPresent(provider)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
OrganizationModel organization = (OrganizationModel) session.getAttribute(OrganizationModel.class.getName());
|
OrganizationModel organization = (OrganizationModel) session.getAttribute(OrganizationModel.class.getName());
|
||||||
|
|
||||||
if (organization == null) {
|
if (organization == null || !organization.isEnabled()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,11 +17,10 @@
|
||||||
|
|
||||||
package org.keycloak.organization.authentication.authenticators.browser;
|
package org.keycloak.organization.authentication.authenticators.browser;
|
||||||
|
|
||||||
|
import static org.keycloak.organization.utils.Organizations.isEnabledAndOrganizationsPresent;
|
||||||
import static org.keycloak.organization.utils.Organizations.resolveBroker;
|
import static org.keycloak.organization.utils.Organizations.resolveBroker;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import jakarta.ws.rs.core.MultivaluedMap;
|
import jakarta.ws.rs.core.MultivaluedMap;
|
||||||
import org.keycloak.authentication.AuthenticationFlowContext;
|
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||||
import org.keycloak.authentication.AuthenticationFlowError;
|
import org.keycloak.authentication.AuthenticationFlowError;
|
||||||
|
@ -30,7 +29,6 @@ import org.keycloak.forms.login.LoginFormsProvider;
|
||||||
import org.keycloak.forms.login.freemarker.model.AuthenticationContextBean;
|
import org.keycloak.forms.login.freemarker.model.AuthenticationContextBean;
|
||||||
import org.keycloak.forms.login.freemarker.model.IdentityProviderBean;
|
import org.keycloak.forms.login.freemarker.model.IdentityProviderBean;
|
||||||
import org.keycloak.http.HttpRequest;
|
import org.keycloak.http.HttpRequest;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
|
@ -56,7 +54,7 @@ public class OrganizationAuthenticator extends IdentityProviderAuthenticator {
|
||||||
public void authenticate(AuthenticationFlowContext context) {
|
public void authenticate(AuthenticationFlowContext context) {
|
||||||
OrganizationProvider provider = getOrganizationProvider();
|
OrganizationProvider provider = getOrganizationProvider();
|
||||||
|
|
||||||
if (!provider.isEnabled()) {
|
if (!isEnabledAndOrganizationsPresent(provider)) {
|
||||||
context.attempted();
|
context.attempted();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,8 @@ import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.representations.IDToken;
|
import org.keycloak.representations.IDToken;
|
||||||
|
|
||||||
|
import static org.keycloak.organization.utils.Organizations.isEnabledAndOrganizationsPresent;
|
||||||
|
|
||||||
public class OrganizationMembershipMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper, EnvironmentDependentProviderFactory {
|
public class OrganizationMembershipMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper, EnvironmentDependentProviderFactory {
|
||||||
|
|
||||||
public static final String PROVIDER_ID = "oidc-organization-membership-mapper";
|
public static final String PROVIDER_ID = "oidc-organization-membership-mapper";
|
||||||
|
@ -77,7 +79,7 @@ public class OrganizationMembershipMapper extends AbstractOIDCProtocolMapper imp
|
||||||
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) {
|
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) {
|
||||||
OrganizationProvider provider = keycloakSession.getProvider(OrganizationProvider.class);
|
OrganizationProvider provider = keycloakSession.getProvider(OrganizationProvider.class);
|
||||||
|
|
||||||
if (!provider.isEnabled()) {
|
if (!isEnabledAndOrganizationsPresent(provider)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
package org.keycloak.organization.protocol.mappers.saml;
|
package org.keycloak.organization.protocol.mappers.saml;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.keycloak.Config.Scope;
|
import org.keycloak.Config.Scope;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.common.Profile.Feature;
|
import org.keycloak.common.Profile.Feature;
|
||||||
|
@ -39,6 +38,8 @@ import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||||
|
|
||||||
|
import static org.keycloak.organization.utils.Organizations.isEnabledAndOrganizationsPresent;
|
||||||
|
|
||||||
public class OrganizationMembershipMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper, EnvironmentDependentProviderFactory {
|
public class OrganizationMembershipMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper, EnvironmentDependentProviderFactory {
|
||||||
|
|
||||||
public static final String ID = "saml-organization-membership-mapper";
|
public static final String ID = "saml-organization-membership-mapper";
|
||||||
|
@ -59,7 +60,7 @@ public class OrganizationMembershipMapper extends AbstractSAMLProtocolMapper imp
|
||||||
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
|
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
|
||||||
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
|
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
|
||||||
|
|
||||||
if (!provider.isEnabled()) {
|
if (!isEnabledAndOrganizationsPresent(provider)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,4 +114,9 @@ public class Organizations {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isEnabledAndOrganizationsPresent(OrganizationProvider organizationProvider) {
|
||||||
|
// todo replace getAllStream().findAny().isPresent() with count query
|
||||||
|
return organizationProvider != null && organizationProvider.isEnabled() && organizationProvider.getAllStream().findAny().isPresent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,6 @@ import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenShift 4 Identity Provider configuration class.
|
* OpenShift 4 Identity Provider configuration class.
|
||||||
|
|
|
@ -179,4 +179,9 @@ public class RealmAttributeUpdater extends ServerResourceUpdater<RealmAttributeU
|
||||||
rep.getBrowserSecurityHeaders().put(name, value);
|
rep.getBrowserSecurityHeaders().put(name, value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RealmAttributeUpdater setOrganizationEnabled(Boolean organizationsEnabled) {
|
||||||
|
rep.setOrganizationsEnabled(organizationsEnabled);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
package org.keycloak.testsuite.organization.admin;
|
package org.keycloak.testsuite.organization.admin;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
@ -24,7 +26,6 @@ import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import jakarta.ws.rs.core.Response.Status;
|
import jakarta.ws.rs.core.Response.Status;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
|
@ -48,6 +49,7 @@ import org.keycloak.testsuite.pages.AppPage;
|
||||||
import org.keycloak.testsuite.pages.IdpConfirmLinkPage;
|
import org.keycloak.testsuite.pages.IdpConfirmLinkPage;
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
import org.keycloak.testsuite.pages.UpdateAccountInformationPage;
|
import org.keycloak.testsuite.pages.UpdateAccountInformationPage;
|
||||||
|
import org.keycloak.testsuite.util.TestCleanup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
@ -78,6 +80,7 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
testRealm.getClients().addAll(bc.createConsumerClients());
|
testRealm.getClients().addAll(bc.createConsumerClients());
|
||||||
testRealm.setSmtpServer(null);
|
testRealm.setSmtpServer(null);
|
||||||
|
testRealm.setOrganizationsEnabled(Boolean.TRUE);
|
||||||
super.configureTestRealm(testRealm);
|
super.configureTestRealm(testRealm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,25 +99,28 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected OrganizationRepresentation createOrganization(String name, String... orgDomain) {
|
protected OrganizationRepresentation createOrganization(String name, String... orgDomain) {
|
||||||
|
return createOrganization(testRealm(), getCleanup(), name, brokerConfigFunction.apply(name).setUpIdentityProvider(), orgDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static OrganizationRepresentation createOrganization(RealmResource testRealm, TestCleanup testCleanup, String name, IdentityProviderRepresentation broker, String... orgDomain) {
|
||||||
OrganizationRepresentation org = createRepresentation(name, orgDomain);
|
OrganizationRepresentation org = createRepresentation(name, orgDomain);
|
||||||
String id;
|
String id;
|
||||||
|
|
||||||
try (Response response = testRealm().organizations().create(org)) {
|
try (Response response = testRealm.organizations().create(org)) {
|
||||||
assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
|
assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
|
||||||
id = ApiUtil.getCreatedId(response);
|
id = ApiUtil.getCreatedId(response);
|
||||||
}
|
}
|
||||||
IdentityProviderRepresentation broker = brokerConfigFunction.apply(name).setUpIdentityProvider();
|
|
||||||
broker.getConfig().put(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE, org.getDomains().iterator().next().getName());
|
broker.getConfig().put(OrganizationModel.ORGANIZATION_DOMAIN_ATTRIBUTE, org.getDomains().iterator().next().getName());
|
||||||
testRealm().identityProviders().create(broker).close();
|
testRealm.identityProviders().create(broker).close();
|
||||||
getCleanup().addCleanup(testRealm().identityProviders().get(broker.getAlias())::remove);
|
testCleanup.addCleanup(testRealm.identityProviders().get(broker.getAlias())::remove);
|
||||||
testRealm().organizations().get(id).identityProviders().addIdentityProvider(broker.getAlias()).close();
|
testRealm.organizations().get(id).identityProviders().addIdentityProvider(broker.getAlias()).close();
|
||||||
org = testRealm().organizations().get(id).toRepresentation();
|
org = testRealm.organizations().get(id).toRepresentation();
|
||||||
getCleanup().addCleanup(() -> testRealm().organizations().get(id).delete().close());
|
testCleanup.addCleanup(() -> testRealm.organizations().get(id).delete().close());
|
||||||
|
|
||||||
return org;
|
return org;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected OrganizationRepresentation createRepresentation(String name, String... orgDomains) {
|
protected static OrganizationRepresentation createRepresentation(String name, String... orgDomains) {
|
||||||
OrganizationRepresentation org = new OrganizationRepresentation();
|
OrganizationRepresentation org = new OrganizationRepresentation();
|
||||||
org.setName(name);
|
org.setName(name);
|
||||||
|
|
||||||
|
@ -188,6 +194,7 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
|
||||||
log.debug("Updating info on updateAccount page");
|
log.debug("Updating info on updateAccount page");
|
||||||
assertFalse(driver.getPageSource().contains("kc.org"));
|
assertFalse(driver.getPageSource().contains("kc.org"));
|
||||||
updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), email, "Firstname", "Lastname");
|
updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), email, "Firstname", "Lastname");
|
||||||
|
assertThat(appPage.getRequestType(),is(AppPage.RequestType.AUTH_RESPONSE));
|
||||||
|
|
||||||
assertIsMember(email, organization);
|
assertIsMember(email, organization);
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ public class OrganizationAdminPermissionsTest extends AbstractOrganizationTest {
|
||||||
.role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.MANAGE_IDENTITY_PROVIDERS)
|
.role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.MANAGE_IDENTITY_PROVIDERS)
|
||||||
.role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.MANAGE_USERS)
|
.role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.MANAGE_USERS)
|
||||||
.build());
|
.build());
|
||||||
|
super.configureTestRealm(testRealm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -17,14 +17,18 @@
|
||||||
|
|
||||||
package org.keycloak.testsuite.organization.admin;
|
package org.keycloak.testsuite.organization.admin;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
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.common.Profile.Feature;
|
import org.keycloak.common.Profile.Feature;
|
||||||
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.arquillian.annotation.EnableFeature;
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
|
import org.keycloak.testsuite.updaters.RealmAttributeUpdater;
|
||||||
|
|
||||||
@EnableFeature(Feature.ORGANIZATION)
|
@EnableFeature(Feature.ORGANIZATION)
|
||||||
public class OrganizationMemberAuthenticationTest extends AbstractOrganizationTest {
|
public class OrganizationMemberAuthenticationTest extends AbstractOrganizationTest {
|
||||||
|
@ -86,4 +90,36 @@ public class OrganizationMemberAuthenticationTest extends AbstractOrganizationTe
|
||||||
Assert.assertTrue(loginPage.isUsernameInputPresent());
|
Assert.assertTrue(loginPage.isUsernameInputPresent());
|
||||||
Assert.assertTrue(loginPage.isPasswordInputPresent());
|
Assert.assertTrue(loginPage.isPasswordInputPresent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthenticateUnmanagedMemberWehnProviderDisabled() throws IOException {
|
||||||
|
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||||
|
UserRepresentation member = addMember(organization, "contractor@contractor.org");
|
||||||
|
|
||||||
|
// first try to access login page
|
||||||
|
oauth.clientId("broker-app");
|
||||||
|
loginPage.open(bc.consumerRealmName());
|
||||||
|
Assert.assertFalse(loginPage.isPasswordInputPresent());
|
||||||
|
Assert.assertFalse(loginPage.isSocialButtonPresent(bc.getIDPAlias()));
|
||||||
|
|
||||||
|
// disable the organization provider
|
||||||
|
try (RealmAttributeUpdater rau = new RealmAttributeUpdater(testRealm())
|
||||||
|
.setOrganizationEnabled(Boolean.FALSE)
|
||||||
|
.update()) {
|
||||||
|
|
||||||
|
// access the page again, now it should be present username and password fields
|
||||||
|
loginPage.open(bc.consumerRealmName());
|
||||||
|
|
||||||
|
waitForPage(driver, "sign in to", true);
|
||||||
|
assertThat("Driver should be on the consumer realm page right now",
|
||||||
|
driver.getCurrentUrl(), Matchers.containsString("/auth/realms/" + bc.consumerRealmName() + "/"));
|
||||||
|
Assert.assertTrue(loginPage.isPasswordInputPresent());
|
||||||
|
// no idp should be shown because there is only a single idp that is bound to an organization
|
||||||
|
Assert.assertFalse(loginPage.isSocialButtonPresent(bc.getIDPAlias()));
|
||||||
|
|
||||||
|
// the member should be able to log in using the credentials
|
||||||
|
loginPage.login(member.getEmail(), memberPassword);
|
||||||
|
appPage.assertCurrent();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -39,6 +39,7 @@ 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;
|
||||||
import jakarta.ws.rs.core.Response.Status;
|
import jakarta.ws.rs.core.Response.Status;
|
||||||
|
import java.io.IOException;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.OrganizationMemberResource;
|
import org.keycloak.admin.client.resource.OrganizationMemberResource;
|
||||||
|
@ -57,6 +58,7 @@ import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy;
|
import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
|
import org.keycloak.testsuite.updaters.RealmAttributeUpdater;
|
||||||
|
|
||||||
@EnableFeature(Feature.ORGANIZATION)
|
@EnableFeature(Feature.ORGANIZATION)
|
||||||
public class OrganizationMemberTest extends AbstractOrganizationTest {
|
public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
|
@ -194,7 +196,7 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
assertThat(existingOrg.isEnabled(), is(false));
|
assertThat(existingOrg.isEnabled(), is(false));
|
||||||
|
|
||||||
// now fetch all users from the org - unmanaged users should still be enabled, but managed ones should not.
|
// now fetch all users from the org - unmanaged users should still be enabled, but managed ones should not.
|
||||||
List<UserRepresentation> existing = organization.members().getAll();;
|
List<UserRepresentation> existing = organization.members().getAll();
|
||||||
assertThat(existing, not(empty()));
|
assertThat(existing, not(empty()));
|
||||||
assertThat(existing, hasSize(6));
|
assertThat(existing, hasSize(6));
|
||||||
for (UserRepresentation user : existing) {
|
for (UserRepresentation user : existing) {
|
||||||
|
@ -229,6 +231,48 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAllDisabledOrganizationProvider() throws IOException {
|
||||||
|
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());
|
||||||
|
|
||||||
|
// now fetch all users from the realm
|
||||||
|
List<UserRepresentation> members = testRealm().users().search("*neworg*", null, null);
|
||||||
|
members.stream().forEach(user -> assertThat(user.isEnabled(), is(Boolean.TRUE)));
|
||||||
|
|
||||||
|
// disable the organization provider
|
||||||
|
try (RealmAttributeUpdater rau = new RealmAttributeUpdater(testRealm())
|
||||||
|
.setOrganizationEnabled(Boolean.FALSE)
|
||||||
|
.update()) {
|
||||||
|
|
||||||
|
// now fetch all members from the realm - unmanaged users should still be enabled, but managed ones should not.
|
||||||
|
List<UserRepresentation> existing = testRealm().users().search("*neworg*", null, null);
|
||||||
|
assertThat(existing, hasSize(members.size()));
|
||||||
|
for (UserRepresentation user : existing) {
|
||||||
|
if (user.getEmail().equals(bc.getUserEmail())) {
|
||||||
|
assertThat(user.isEnabled(), is(Boolean.FALSE));
|
||||||
|
|
||||||
|
// try to update the disabled user (for example, try to re-enable the user) - should not be possible.
|
||||||
|
user.setEnabled(Boolean.TRUE);
|
||||||
|
try {
|
||||||
|
testRealm().users().get(user.getId()).update(user);
|
||||||
|
fail("Should not be possible to update disabled org user");
|
||||||
|
} catch(BadRequestException expected) {}
|
||||||
|
} else {
|
||||||
|
assertThat("User " + user.getUsername(), user.isEnabled(), is(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteUnmanagedMember() {
|
public void testDeleteUnmanagedMember() {
|
||||||
UPConfig upConfig = testRealm().users().userProfile().getConfiguration();
|
UPConfig upConfig = testRealm().users().userProfile().getConfiguration();
|
||||||
|
|
|
@ -41,6 +41,7 @@ import java.util.stream.Collectors;
|
||||||
import jakarta.ws.rs.NotFoundException;
|
import jakarta.ws.rs.NotFoundException;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import jakarta.ws.rs.core.Response.Status;
|
import jakarta.ws.rs.core.Response.Status;
|
||||||
|
import java.io.IOException;
|
||||||
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.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
@ -51,6 +52,7 @@ import org.keycloak.representations.idm.OrganizationDomainRepresentation;
|
||||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
|
import org.keycloak.testsuite.updaters.RealmAttributeUpdater;
|
||||||
import org.keycloak.testsuite.util.RealmBuilder;
|
import org.keycloak.testsuite.util.RealmBuilder;
|
||||||
|
|
||||||
@EnableFeature(Feature.ORGANIZATION)
|
@EnableFeature(Feature.ORGANIZATION)
|
||||||
|
@ -373,35 +375,67 @@ public class OrganizationTest extends AbstractOrganizationTest {
|
||||||
assertNotNull(existing.getDomain("acme.com"));
|
assertNotNull(existing.getDomain("acme.com"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDisabledOrganizationProvider() throws IOException {
|
||||||
|
OrganizationRepresentation existing = createOrganization("acme", "acme.org", "acme.net");
|
||||||
|
// disable the organization provider and try to access REST endpoints
|
||||||
|
try (RealmAttributeUpdater rau = new RealmAttributeUpdater(testRealm())
|
||||||
|
.setOrganizationEnabled(Boolean.FALSE)
|
||||||
|
.update()) {
|
||||||
|
OrganizationRepresentation org = createRepresentation("some", "some.com");
|
||||||
|
|
||||||
|
try (Response response = testRealm().organizations().create(org)) {
|
||||||
|
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
testRealm().organizations().getAll();
|
||||||
|
fail("Expected NotFoundException");
|
||||||
|
} catch (NotFoundException expected) {}
|
||||||
|
try {
|
||||||
|
testRealm().organizations().search("*");
|
||||||
|
fail("Expected NotFoundException");
|
||||||
|
} catch (NotFoundException expected) {}
|
||||||
|
try {
|
||||||
|
testRealm().organizations().get(existing.getId()).toRepresentation();
|
||||||
|
fail("Expected NotFoundException");
|
||||||
|
} catch (NotFoundException expected) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteRealm() {
|
public void testDeleteRealm() {
|
||||||
RealmRepresentation realmRep = RealmBuilder.create().name(KeycloakModelUtils.generateId()).build();
|
RealmRepresentation realmRep = RealmBuilder.create()
|
||||||
RealmResource realm = realmsResouce().realm(realmRep.getRealm());
|
.name(KeycloakModelUtils.generateId())
|
||||||
|
.organizationEnabled(true)
|
||||||
|
.build();
|
||||||
|
RealmResource realmRes = realmsResouce().realm(realmRep.getRealm());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
realmRep.setEnabled(true);
|
realmRep.setEnabled(true);
|
||||||
realmsResouce().create(realmRep);
|
realmsResouce().create(realmRep);
|
||||||
realm = realmsResouce().realm(realmRep.getRealm());
|
realmRes = realmsResouce().realm(realmRep.getRealm());
|
||||||
realm.toRepresentation();
|
realmRes.toRepresentation();
|
||||||
OrganizationRepresentation org = new OrganizationRepresentation();
|
OrganizationRepresentation org = new OrganizationRepresentation();
|
||||||
org.setName("test-org");
|
org.setName("test-org");
|
||||||
org.addDomain(new OrganizationDomainRepresentation("test.org"));
|
org.addDomain(new OrganizationDomainRepresentation("test.org"));
|
||||||
org.setEnabled(true);
|
org.setEnabled(true);
|
||||||
Response response = realm.organizations().create(org);
|
try (Response response = realmRes.organizations().create(org)) {
|
||||||
response.close();
|
assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
|
||||||
assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
|
}
|
||||||
List<OrganizationRepresentation> orgs = realm.organizations().getAll();
|
|
||||||
assertEquals(1, orgs.size());
|
List<OrganizationRepresentation> orgs = realmRes.organizations().getAll();
|
||||||
|
assertThat(orgs, hasSize(1));
|
||||||
|
|
||||||
IdentityProviderRepresentation broker = bc.setUpIdentityProvider();
|
IdentityProviderRepresentation broker = bc.setUpIdentityProvider();
|
||||||
broker.setAlias(KeycloakModelUtils.generateId());
|
broker.setAlias(KeycloakModelUtils.generateId());
|
||||||
response = realm.identityProviders().create(broker);
|
try (Response response = realmRes.identityProviders().create(broker)) {
|
||||||
response.close();
|
assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
|
||||||
assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
|
}
|
||||||
response = realm.organizations().get(orgs.get(0).getId()).identityProviders().addIdentityProvider(broker.getAlias());
|
try (Response response = realmRes.organizations().get(orgs.get(0).getId()).identityProviders().addIdentityProvider(broker.getAlias())) {
|
||||||
response.close();
|
assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
|
||||||
assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
|
}
|
||||||
} finally {
|
} finally {
|
||||||
realm.remove();
|
realmRes.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -326,4 +326,9 @@ public class RealmBuilder {
|
||||||
rep.setDefaultLocale(defaultLocale);
|
rep.setDefaultLocale(defaultLocale);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RealmBuilder organizationEnabled(boolean enabled) {
|
||||||
|
rep.setOrganizationsEnabled(enabled);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue