KEYCLOAK-14209 Client policies admin console support. Changing of format of JSON for client policies and profiles. Remove support for default policies (#7969)

* KEYCLOAK-14209 KEYCLOAK-17988 Client policies admin console support. Changing of format of JSON for client policies and profiles. Refactoring based on feedback and remove builtin policies
This commit is contained in:
Marek Posolda 2021-05-12 16:19:55 +02:00 committed by GitHub
parent f37a24dd91
commit a6d4316084
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
99 changed files with 3267 additions and 1993 deletions

View file

@ -17,18 +17,19 @@
package org.keycloak.representations.idm; package org.keycloak.representations.idm;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.JsonNode;
import org.keycloak.util.JsonSerialization;
/** /**
* Client Policies' (the set of all Client Policy) external representation class * Client Policies' (the set of all Client Policy) external representation class
* *
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
@JsonIgnoreProperties(ignoreUnknown = true)
public class ClientPoliciesRepresentation { public class ClientPoliciesRepresentation {
protected List<ClientPolicyRepresentation> policies; protected List<ClientPolicyRepresentation> policies = new ArrayList<>();
public List<ClientPolicyRepresentation> getPolicies() { public List<ClientPolicyRepresentation> getPolicies() {
return policies; return policies;
@ -38,4 +39,17 @@ public class ClientPoliciesRepresentation {
this.policies = policies; this.policies = policies;
} }
@Override
public int hashCode() {
return JsonSerialization.mapper.convertValue(this, JsonNode.class).hashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ClientPoliciesRepresentation)) return false;
JsonNode jsonNode = JsonSerialization.mapper.convertValue(this, JsonNode.class);
JsonNode jsonNodeThat = JsonSerialization.mapper.convertValue(obj, JsonNode.class);
return jsonNode.equals(jsonNodeThat);
}
} }

View file

@ -0,0 +1,57 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.representations.idm;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Just adds some type-safety to the ClientPolicyConditionConfiguration
*
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/
public class ClientPolicyConditionConfigurationRepresentation {
private Map<String, Object> configAsMap = new HashMap<>();
@JsonProperty("is-negative-logic")
private Boolean negativeLogic;
public Boolean isNegativeLogic() {
return negativeLogic;
}
public void setNegativeLogic(Boolean negativeLogic) {
this.negativeLogic = negativeLogic;
}
@JsonAnyGetter
public Map<String, Object> getConfigAsMap() {
return configAsMap;
}
@JsonAnySetter
public void setConfigAsMap(String name, Object value) {
this.configAsMap.put(name, value);
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.representations.idm;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ClientPolicyConditionRepresentation {
@JsonProperty("condition")
private String conditionProviderId;
private ClientPolicyConditionConfigurationRepresentation configuration;
public ClientPolicyConditionRepresentation() {
}
public ClientPolicyConditionRepresentation(String conditionProviderId, ClientPolicyConditionConfigurationRepresentation configuration) {
this.conditionProviderId = conditionProviderId;
this.configuration = configuration;
}
public String getConditionProviderId() {
return conditionProviderId;
}
public void setConditionProviderId(String conditionProviderId) {
this.conditionProviderId = conditionProviderId;
}
public ClientPolicyConditionConfigurationRepresentation getConfiguration() {
return configuration;
}
public void setConfiguration(ClientPolicyConditionConfigurationRepresentation configuration) {
this.configuration = configuration;
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.representations.idm;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
/**
* Just adds some type-safety to the ClientPolicyExecutorConfiguration
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ClientPolicyExecutorConfigurationRepresentation {
private Map<String, Object> configAsMap = new HashMap<>();
@JsonAnyGetter
public Map<String, Object> getConfigAsMap() {
return configAsMap;
}
@JsonAnySetter
public void setConfigAsMap(String name, Object value) {
this.configAsMap.put(name, value);
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.representations.idm;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ClientPolicyExecutorRepresentation {
@JsonProperty("executor")
private String executorProviderId;
private ClientPolicyExecutorConfigurationRepresentation configuration;
public ClientPolicyExecutorRepresentation() {
}
public ClientPolicyExecutorRepresentation(String executorProviderId, ClientPolicyExecutorConfigurationRepresentation configuration) {
this.executorProviderId = executorProviderId;
this.configuration = configuration;
}
public String getExecutorProviderId() {
return executorProviderId;
}
public void setExecutorProviderId(String providerId) {
this.executorProviderId = providerId;
}
public ClientPolicyExecutorConfigurationRepresentation getConfiguration() {
return configuration;
}
public void setConfiguration(ClientPolicyExecutorConfigurationRepresentation configuration) {
this.configuration = configuration;
}
}

View file

@ -19,21 +19,17 @@ package org.keycloak.representations.idm;
import java.util.List; import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/** /**
* Client Policy's external representation class * Client Policy's external representation class
* *
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
@JsonIgnoreProperties(ignoreUnknown = true)
public class ClientPolicyRepresentation { public class ClientPolicyRepresentation {
protected String name; protected String name;
protected String description; protected String description;
protected Boolean builtin; protected Boolean enabled;
protected Boolean enable; protected List<ClientPolicyConditionRepresentation> conditions;
protected List<Object> conditions;
protected List<String> profiles; protected List<String> profiles;
public String getName() { public String getName() {
@ -52,27 +48,19 @@ public class ClientPolicyRepresentation {
this.description = description; this.description = description;
} }
public Boolean isBuiltin() { public Boolean isEnabled() {
return builtin; return enabled;
} }
public void setBuiltin(Boolean builtin) { public void setEnabled(Boolean enabled) {
this.builtin = builtin; this.enabled = enabled;
} }
public Boolean isEnable() { public List<ClientPolicyConditionRepresentation> getConditions() {
return enable;
}
public void setEnable(Boolean enable) {
this.enable = enable;
}
public List<Object> getConditions() {
return conditions; return conditions;
} }
public void setConditions(List<Object> conditions) { public void setConditions(List<ClientPolicyConditionRepresentation> conditions) {
this.conditions = conditions; this.conditions = conditions;
} }

View file

@ -19,20 +19,16 @@ package org.keycloak.representations.idm;
import java.util.List; import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/** /**
* Client Profile's external representation class * Client Profile's external representation class
* *
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
@JsonIgnoreProperties(ignoreUnknown = true)
public class ClientProfileRepresentation { public class ClientProfileRepresentation {
protected String name; protected String name;
protected String description; protected String description;
protected Boolean builtin; protected List<ClientPolicyExecutorRepresentation> executors;
protected List<Object> executors;
public String getName() { public String getName() {
return name; return name;
@ -50,19 +46,11 @@ public class ClientProfileRepresentation {
this.description = description; this.description = description;
} }
public Boolean isBuiltin() { public List<ClientPolicyExecutorRepresentation> getExecutors() {
return builtin;
}
public void setBuiltin(Boolean builtin) {
this.builtin = builtin;
}
public List<Object> getExecutors() {
return executors; return executors;
} }
public void setExecutors(List<Object> executors) { public void setExecutors(List<ClientPolicyExecutorRepresentation> executors) {
this.executors = executors; this.executors = executors;
} }
} }

View file

@ -17,18 +17,25 @@
package org.keycloak.representations.idm; package org.keycloak.representations.idm;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import org.keycloak.util.JsonSerialization;
/** /**
* Client Profiles' (the set of all Client Profile) external representation class * Client Profiles' (the set of all Client Profile) external representation class
* *
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
@JsonIgnoreProperties(ignoreUnknown = true)
public class ClientProfilesRepresentation { public class ClientProfilesRepresentation {
protected List<ClientProfileRepresentation> profiles;
private List<ClientProfileRepresentation> profiles = new ArrayList<>();
// Global profiles, which are builtin in Keycloak.
@JsonProperty("globalProfiles")
private List<ClientProfileRepresentation> globalProfiles;
public List<ClientProfileRepresentation> getProfiles() { public List<ClientProfileRepresentation> getProfiles() {
return profiles; return profiles;
@ -38,4 +45,24 @@ public class ClientProfilesRepresentation {
this.profiles = profiles; this.profiles = profiles;
} }
public List<ClientProfileRepresentation> getGlobalProfiles() {
return globalProfiles;
}
public void setGlobalProfiles(List<ClientProfileRepresentation> globalProfiles) {
this.globalProfiles = globalProfiles;
}
@Override
public int hashCode() {
return JsonSerialization.mapper.convertValue(this, JsonNode.class).hashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ClientProfilesRepresentation)) return false;
JsonNode jsonNode = JsonSerialization.mapper.convertValue(this, JsonNode.class);
JsonNode jsonNodeThat = JsonSerialization.mapper.convertValue(obj, JsonNode.class);
return jsonNode.equals(jsonNodeThat);
}
} }

View file

@ -5,9 +5,9 @@ import javax.ws.rs.GET;
import javax.ws.rs.PUT; import javax.ws.rs.PUT;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.ClientPoliciesRepresentation;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
@ -17,10 +17,10 @@ public interface ClientPoliciesPoliciesResource {
@GET @GET
@NoCache @NoCache
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
String getPolicies(); ClientPoliciesRepresentation getPolicies();
@PUT @PUT
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
Response updatePolicies(final String json); void updatePolicies(final ClientPoliciesRepresentation clientPolicies);
} }

View file

@ -4,10 +4,11 @@ import javax.ws.rs.Consumes;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.PUT; import javax.ws.rs.PUT;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.ClientProfilesRepresentation;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
@ -17,9 +18,14 @@ public interface ClientPoliciesProfilesResource {
@GET @GET
@NoCache @NoCache
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
String getProfiles(); ClientProfilesRepresentation getProfiles(@QueryParam("include-global-profiles") Boolean includeGlobalProfiles);
/**
* Update client profiles in the realm. The "globalProfiles" field of clientProfiles is ignored as it is not possible to update global profiles
*
* @param clientProfiles
*/
@PUT @PUT
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
Response updateProfiles(final String json); void updateProfiles(final ClientProfilesRepresentation clientProfiles);
} }

View file

@ -50,7 +50,6 @@ public class QuarkusKeycloakApplication extends KeycloakApplication {
QuarkusKeycloakSessionFactory instance = QuarkusKeycloakSessionFactory.getInstance(); QuarkusKeycloakSessionFactory instance = QuarkusKeycloakSessionFactory.getInstance();
sessionFactory = instance; sessionFactory = instance;
instance.init(); instance.init();
instance.create().clientPolicy().setupClientPoliciesOnKeycloakApp("/keycloak-default-client-profiles.json", "/keycloak-default-client-policies.json");
sessionFactory.publish(new PostMigrationEvent()); sessionFactory.publish(new PostMigrationEvent());
} }

View file

@ -120,4 +120,8 @@ public final class Constants {
*/ */
public static final String STORAGE_BATCH_SIZE = "org.keycloak.storage.batch_size"; public static final String STORAGE_BATCH_SIZE = "org.keycloak.storage.batch_size";
// Client Polices Realm Attributes Keys
public static final String CLIENT_PROFILES = "client-policies.profiles";
public static final String CLIENT_POLICIES = "client-policies.policies";
} }

View file

@ -95,8 +95,8 @@ public class ModelToRepresentation {
REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyAvoidSameAuthenticatorRegisterPasswordless"); REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyAvoidSameAuthenticatorRegisterPasswordless");
REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyAcceptableAaguidsPasswordless"); REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyAcceptableAaguidsPasswordless");
REALM_EXCLUDED_ATTRIBUTES.add("client-policies.profiles"); REALM_EXCLUDED_ATTRIBUTES.add(Constants.CLIENT_POLICIES);
REALM_EXCLUDED_ATTRIBUTES.add("client-policies.policies"); REALM_EXCLUDED_ATTRIBUTES.add(Constants.CLIENT_PROFILES);
} }
@ -295,7 +295,7 @@ public class ModelToRepresentation {
return rep; return rep;
} }
public static RealmRepresentation toRepresentation(RealmModel realm, boolean internal) { public static RealmRepresentation toRepresentation(KeycloakSession session, RealmModel realm, boolean internal) {
RealmRepresentation rep = new RealmRepresentation(); RealmRepresentation rep = new RealmRepresentation();
rep.setId(realm.getId()); rep.setId(realm.getId());
rep.setRealm(realm.getName()); rep.setRealm(realm.getName());
@ -447,6 +447,8 @@ public class ModelToRepresentation {
exportGroups(realm, rep); exportGroups(realm, rep);
} }
session.clientPolicy().updateRealmRepresentationFromModel(realm, rep);
rep.setAttributes(stripRealmAttributesIncludedAsFields(realm.getAttributes())); rep.setAttributes(stripRealmAttributesIncludedAsFields(realm.getAttributes()));
if (!internal) { if (!internal) {

View file

@ -1181,6 +1181,7 @@ public class RepresentationToModel {
realm.setWebAuthnPolicyPasswordless(webAuthnPolicy); realm.setWebAuthnPolicyPasswordless(webAuthnPolicy);
updateCibaSettings(rep, realm); updateCibaSettings(rep, realm);
session.clientPolicy().updateRealmModelFromRepresentation(realm, rep);
if (rep.getSmtpServer() != null) { if (rep.getSmtpServer() != null) {
Map<String, String> config = new HashMap(rep.getSmtpServer()); Map<String, String> config = new HashMap(rep.getSmtpServer());

View file

@ -1,762 +0,0 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.services.clientpolicy;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.jboss.logging.Logger;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.ClientPoliciesRepresentation;
import org.keycloak.representations.idm.ClientPolicyRepresentation;
import org.keycloak.representations.idm.ClientProfileRepresentation;
import org.keycloak.representations.idm.ClientProfilesRepresentation;
import org.keycloak.services.clientpolicy.condition.ClientPolicyConditionConfiguration;
import org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProvider;
import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorConfiguration;
import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider;
import org.keycloak.util.JsonSerialization;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Utilities for treating client policies/profiles
*
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/
public class ClientPoliciesUtil {
private static final Logger logger = Logger.getLogger(ClientPoliciesUtil.class);
private static final ObjectMapper objectMapper = new ObjectMapper();
/**
* gets existing client profiles in a realm as representation.
* not return null.
*/
public static ClientProfilesRepresentation getClientProfilesRepresentation(KeycloakSession session, RealmModel realm) throws ClientPolicyException {
ClientProfilesRepresentation profilesRep = null;
String profilesJson = null;
// get existing profiles json
if (realm != null) {
profilesJson = session.clientPolicy().getClientProfilesJsonString(realm);
} else {
// if realm not specified, use builtin profiles set in keycloak's binary.
profilesJson = session.clientPolicy().getClientProfilesOnKeycloakApp();
}
// deserialize existing profiles (json -> representation)
if (profilesJson == null) {
return new ClientProfilesRepresentation();
}
profilesRep = convertClientProfilesJsonToRepresentation(profilesJson);
if (profilesRep == null) {
return new ClientProfilesRepresentation();
}
return profilesRep;
}
/**
* gets existing client profiles in a realm as model.
* not return null.
*/
public static Map<String, ClientProfileModel> getClientProfilesModel(KeycloakSession session, RealmModel realm) {
// get existing profiles as json
String profilesJson = session.clientPolicy().getClientProfilesJsonString(realm);
if (profilesJson == null) {
return Collections.emptyMap();
}
// deserialize existing profiles (json -> representation)
ClientProfilesRepresentation profilesRep = null;
try {
profilesRep = convertClientProfilesJsonToRepresentation(profilesJson);
} catch (ClientPolicyException e) {
logger.warnv("Failed to serialize client profiles json string. err={0}, errDetail={1}", e.getError(), e.getErrorDetail());
return Collections.emptyMap();
}
if (profilesRep == null || profilesRep.getProfiles() == null) {
return Collections.emptyMap();
}
// constructing existing profiles (representation -> model)
Map<String, ClientProfileModel> profileMap = new HashMap<>();
for (ClientProfileRepresentation profileRep : profilesRep.getProfiles()) {
// ignore profile without name
if (profileRep.getName() == null) {
continue;
}
ClientProfileModel profileModel = new ClientProfileModel();
profileModel.setName(profileRep.getName());
profileModel.setDescription(profileRep.getDescription());
if (profileRep.isBuiltin() != null) {
profileModel.setBuiltin(profileRep.isBuiltin().booleanValue());
} else {
profileModel.setBuiltin(false);
}
if (profileRep.getExecutors() == null) {
profileModel.setExecutors(new ArrayList<>());
profileMap.put(profileRep.getName(), profileModel);
continue;
}
List<Object> executors = new ArrayList<>();
if (profileRep.getExecutors() != null) {
profileRep.getExecutors().stream().forEach(obj->{
JsonNode node = objectMapper.convertValue(obj, JsonNode.class);
node.fields().forEachRemaining(executor->{
ClientPolicyExecutorProvider provider = session.getProvider(ClientPolicyExecutorProvider.class, executor.getKey());
if (provider == null) {
// executor's provider not found. just skip it.
return;
}
try {
ClientPolicyExecutorConfiguration configuration = (ClientPolicyExecutorConfiguration) JsonSerialization.mapper.convertValue(executor.getValue(), provider.getExecutorConfigurationClass());
provider.setupConfiguration(configuration);
executors.add(provider);
} catch (IllegalArgumentException iae) {
logger.warnv("failed for Configuration Setup :: error = {0}", iae.getMessage());
}
});
});
}
profileModel.setExecutors(executors);
profileMap.put(profileRep.getName(), profileModel);
}
return profileMap;
}
/**
* get validated and modified builtin client profiles set on keycloak app as representation.
* it is loaded from json file enclosed in keycloak's binary.
* not return null.
*/
public static ClientProfilesRepresentation getValidatedBuiltinClientProfilesRepresentation(KeycloakSession session, InputStream is) throws ClientPolicyException {
// load builtin client profiles representation
ClientProfilesRepresentation proposedProfilesRep = null;
try {
proposedProfilesRep = JsonSerialization.readValue(is, ClientProfilesRepresentation.class);
} catch (Exception e) {
throw new ClientPolicyException("failed to deserialize builtin proposed client profiles json string.", e.getMessage());
}
if (proposedProfilesRep == null) {
return new ClientProfilesRepresentation();
}
// no profile contained (it is valid)
List<ClientProfileRepresentation> proposedProfileRepList = proposedProfilesRep.getProfiles();
if (proposedProfileRepList == null || proposedProfileRepList.isEmpty()) {
return new ClientProfilesRepresentation();
}
// duplicated profile name is not allowed.
if (proposedProfileRepList.size() != proposedProfileRepList.stream().map(i->i.getName()).distinct().count()) {
throw new ClientPolicyException("proposed builtin client profile name duplicated.");
}
// construct validated and modified profiles from builtin profiles in JSON file enclosed in keycloak binary.
ClientProfilesRepresentation updatingProfilesRep = new ClientProfilesRepresentation();
updatingProfilesRep.setProfiles(new ArrayList<>());
List<ClientProfileRepresentation> updatingProfileList = updatingProfilesRep.getProfiles();
for (ClientProfileRepresentation proposedProfileRep : proposedProfilesRep.getProfiles()) {
if (proposedProfileRep.getName() == null) {
throw new ClientPolicyException("client profile without its name not allowed.");
}
// ignore proposed ordinal profile because builtin profile can only be added.
if (proposedProfileRep.isBuiltin() == null || !proposedProfileRep.isBuiltin()) {
throw new ClientPolicyException("ordinal client profile not allowed.");
}
ClientProfileRepresentation profileRep = new ClientProfileRepresentation();
profileRep.setName(proposedProfileRep.getName());
profileRep.setDescription(proposedProfileRep.getDescription());
profileRep.setBuiltin(Boolean.TRUE);
profileRep.setExecutors(new ArrayList<>()); // to prevent returning null
if (proposedProfileRep.getExecutors() != null) {
for (Object executor : proposedProfileRep.getExecutors()) {
if (isValidExecutor(session, executor) == false) {
throw new ClientPolicyException("proposed client profile contains the executor with its invalid configuration.");
}
profileRep.getExecutors().add(executor);
}
}
updatingProfileList.add(profileRep);
}
return updatingProfilesRep;
}
/**
* convert client profiles as representation to json.
* can return null.
*/
public static String convertClientProfilesRepresentationToJson(ClientProfilesRepresentation reps) throws ClientPolicyException {
return convertRepresentationToJson(reps);
}
/**
* convert client profiles as json to representation.
* not return null.
*/
private static ClientProfilesRepresentation convertClientProfilesJsonToRepresentation(String json) throws ClientPolicyException {
return convertJsonToRepresentation(json, ClientProfilesRepresentation.class);
}
/**
* get validated and modified client profiles as json.
* it can be constructed by merging proposed client profiles with existing client profiles.
* can return null.
*/
public static String getValidatedClientProfilesJson(KeycloakSession session, RealmModel realm, ClientProfilesRepresentation proposedProfilesRep) throws ClientPolicyException {
return convertClientProfilesRepresentationToJson(getValidatedClientProfilesRepresentation(session, realm, proposedProfilesRep));
}
/**
* get validated and modified client profiles as representation.
* it can be constructed by merging proposed client profiles with existing client profiles.
* not return null.
*/
private static ClientProfilesRepresentation getValidatedClientProfilesRepresentation(KeycloakSession session, RealmModel realm, ClientProfilesRepresentation proposedProfilesRep) throws ClientPolicyException {
if (proposedProfilesRep == null) {
proposedProfilesRep = new ClientProfilesRepresentation();
}
if (realm == null) {
throw new ClientPolicyException("realm not specified.");
}
// deserialize existing profiles (json -> representation)
ClientProfilesRepresentation existingProfilesRep = null;
String existingProfilesJson = session.clientPolicy().getClientProfilesJsonString(realm);
if (existingProfilesJson != null) {
existingProfilesRep = convertClientProfilesJsonToRepresentation(existingProfilesJson);
if (existingProfilesRep == null) {
existingProfilesRep = new ClientProfilesRepresentation();
}
} else {
existingProfilesRep = new ClientProfilesRepresentation();
}
// no profile contained (it is valid)
// back to initial builtin profiles
List<ClientProfileRepresentation> proposedProfileRepList = proposedProfilesRep.getProfiles();
if (proposedProfileRepList == null || proposedProfileRepList.isEmpty()) {
proposedProfileRepList = new ArrayList<>();
proposedProfilesRep.setProfiles(new ArrayList<>());
}
// duplicated profile name is not allowed.
if (proposedProfileRepList.size() != proposedProfileRepList.stream().map(i->i.getName()).distinct().count()) {
throw new ClientPolicyException("proposed client profile name duplicated.");
}
// construct updating profiles from existing profiles and proposed profiles
ClientProfilesRepresentation updatingProfilesRep = new ClientProfilesRepresentation();
updatingProfilesRep.setProfiles(new ArrayList<>());
List<ClientProfileRepresentation> updatingProfileList = updatingProfilesRep.getProfiles();
// add existing builtin profiles to updating profiles
List<ClientProfileRepresentation> existingProfileList = existingProfilesRep.getProfiles();
if (existingProfileList != null && !existingProfileList.isEmpty()) {
existingProfileList.stream().filter(i->i.isBuiltin()).forEach(i->updatingProfileList.add(i));
}
for (ClientProfileRepresentation proposedProfileRep : proposedProfilesRep.getProfiles()) {
if (proposedProfileRep.getName() == null) {
throw new ClientPolicyException("client profile without its name not allowed.");
}
// newly proposed builtin profile not allowed because builtin profile cannot added/deleted/modified.
if (proposedProfileRep.isBuiltin() != null && proposedProfileRep.isBuiltin()) {
throw new ClientPolicyException("newly builtin proposed client profile not allowed.");
}
// not allow to overwrite builtin profiles
if (updatingProfileList.stream().anyMatch(i->proposedProfileRep.getName().equals(i.getName()))) {
throw new ClientPolicyException("proposed client profile name is the same one of the builtin profile.");
}
// basically, proposed profile totally overrides existing profile
ClientProfileRepresentation profileRep = new ClientProfileRepresentation();
profileRep.setName(proposedProfileRep.getName());
profileRep.setDescription(proposedProfileRep.getDescription());
profileRep.setBuiltin(Boolean.FALSE);
profileRep.setExecutors(new ArrayList<>());
if (proposedProfileRep.getExecutors() != null) {
for (Object executor : proposedProfileRep.getExecutors()) {
if (isValidExecutor(session, executor) == false) {
throw new ClientPolicyException("proposed client profile contains the executor with its invalid configuration.");
}
profileRep.getExecutors().add(executor);
}
}
updatingProfileList.add(profileRep);
}
return updatingProfilesRep;
}
/**
* get validated and modified builtin client profiles in a realm as representation.
* it can be constructed by merging proposed client profiles with existing client profiles.
* not return null.
*/
public static ClientProfilesRepresentation getValidatedClientProfilesRepresentation(KeycloakSession session, RealmModel realm, String profilesJson) throws ClientPolicyException {
if (profilesJson == null) {
throw new ClientPolicyException("no client profiles json.");
}
// deserialize existing profiles (json -> representation)
ClientProfilesRepresentation proposedProfilesRep = convertClientProfilesJsonToRepresentation(profilesJson);
return getValidatedClientProfilesRepresentation(session, realm, proposedProfilesRep);
}
/**
* check whether the proposed executor's provider can be found in keycloak's ClientPolicyExecutorProvider list.
* not return null.
*/
private static boolean isValidExecutor(KeycloakSession session, Object executor) {
return isValidComponent(session, executor, "executor", (String providerId) -> {
Set<String> providerSet = session.listProviderIds(ClientPolicyExecutorProvider.class);
if (providerSet != null && providerSet.contains(providerId)) {
return true;
}
logger.warnv("no executor provider found. providerId = {0}", providerId);
return false;
});
}
/**
* get existing client policies in a realm as representation.
* not return null.
*/
public static ClientPoliciesRepresentation getClientPoliciesRepresentation(KeycloakSession session, RealmModel realm) throws ClientPolicyException {
ClientPoliciesRepresentation policiesRep = null;
String policiesJson = null;
// get existing policies json
if (realm != null) {
policiesJson = session.clientPolicy().getClientPoliciesJsonString(realm);
} else {
// if realm not specified, use builtin policies set in keycloak's binary.
policiesJson = session.clientPolicy().getClientPoliciesOnKeycloakApp();
}
// deserialize existing policies (json -> representation)
if (policiesJson == null) {
return new ClientPoliciesRepresentation();
}
policiesRep = convertClientPoliciesJsonToRepresentation(policiesJson);
if (policiesRep == null) {
return new ClientPoliciesRepresentation();
}
return policiesRep;
}
/**
* get existing enabled client policies in a realm as model.
* not return null.
*/
public static List<ClientPolicyModel> getEnabledClientProfilesModel(KeycloakSession session, RealmModel realm) {
// get existing profiles as json
String policiesJson = session.clientPolicy().getClientPoliciesJsonString(realm);
if (policiesJson == null) {
return Collections.emptyList();
}
// deserialize existing policies (json -> representation)
ClientPoliciesRepresentation policiesRep = null;
try {
policiesRep = convertClientPoliciesJsonToRepresentation(policiesJson);
} catch (ClientPolicyException e) {
logger.warnv("Failed to serialize client policies json string. err={0}, errDetail={1}", e.getError(), e.getErrorDetail());
return Collections.emptyList();
}
if (policiesRep == null || policiesRep.getPolicies() == null) {
return Collections.emptyList();
}
// constructing existing policies (representation -> model)
List<ClientPolicyModel> policyList = new ArrayList<>();
for (ClientPolicyRepresentation policyRep: policiesRep.getPolicies()) {
// ignore policy without name
if (policyRep.getName() == null) {
continue;
}
// pick up only enabled policy
if (policyRep.isEnable() == null || policyRep.isEnable() == false) {
continue;
}
ClientPolicyModel policyModel = new ClientPolicyModel();
policyModel.setName(policyRep.getName());
policyModel.setDescription(policyRep.getDescription());
policyModel.setEnable(true);
if (policyRep.isBuiltin() != null) {
policyModel.setBuiltin(policyRep.isBuiltin().booleanValue());
} else {
policyModel.setBuiltin(false);
}
List<Object> conditions = new ArrayList<>();
if (policyRep.getConditions() != null) {
policyRep.getConditions().stream().forEach(obj->{
JsonNode node = objectMapper.convertValue(obj, JsonNode.class);
node.fields().forEachRemaining(condition->{
ClientPolicyConditionProvider provider = session.getProvider(ClientPolicyConditionProvider.class, condition.getKey());
if (provider == null) {
// condition's provider not found. just skip it.
return;
}
try {
ClientPolicyConditionConfiguration configuration = (ClientPolicyConditionConfiguration) JsonSerialization.mapper.convertValue(condition.getValue(), provider.getConditionConfigurationClass());
provider.setupConfiguration(configuration);
conditions.add(provider);
} catch (IllegalArgumentException iae) {
logger.warnv("failed for Configuration Setup :: error = {0}", iae.getMessage());
}
});
});
}
policyModel.setConditions(conditions);
if (policyRep.getProfiles() != null) {
policyModel.setProfiles(policyRep.getProfiles().stream().collect(Collectors.toList()));
}
policyList.add(policyModel);
}
return policyList;
}
/**
* get validated and modified builtin client policies set on keycloak app as representation.
* it is loaded from json file enclosed in keycloak's binary.
* not return null.
*/
public static ClientPoliciesRepresentation getValidatedBuiltinClientPoliciesRepresentation(KeycloakSession session, InputStream is) throws ClientPolicyException {
// load builtin client policies representation
ClientPoliciesRepresentation proposedPoliciesRep = null;
try {
proposedPoliciesRep = JsonSerialization.readValue(is, ClientPoliciesRepresentation.class);
} catch (Exception e) {
throw new ClientPolicyException("failed to deserialize builtin proposed client policies json string.", e.getMessage());
}
if (proposedPoliciesRep == null) {
proposedPoliciesRep = new ClientPoliciesRepresentation();
}
// no policy contained (it is valid)
List<ClientPolicyRepresentation> proposedPolicyRepList = proposedPoliciesRep.getPolicies();
if (proposedPolicyRepList == null || proposedPolicyRepList.isEmpty()) {
return new ClientPoliciesRepresentation();
}
// duplicated policy name is not allowed.
if (proposedPolicyRepList.size() != proposedPolicyRepList.stream().map(i->i.getName()).distinct().count()) {
throw new ClientPolicyException("proposed builtin client policy name duplicated.");
}
// construct validated and modified policies from builtin profiles in JSON file enclosed in keycloak binary.
ClientPoliciesRepresentation updatingPoliciesRep = new ClientPoliciesRepresentation();
updatingPoliciesRep.setPolicies(new ArrayList<>());
List<ClientPolicyRepresentation> updatingPoliciesList = updatingPoliciesRep.getPolicies();
for (ClientPolicyRepresentation proposedPolicyRep : proposedPoliciesRep.getPolicies()) {
if (proposedPolicyRep.getName() == null) {
throw new ClientPolicyException("proposed client policy name missing.");
}
// ignore proposed ordinal policy because builtin policy can only be added.
if (proposedPolicyRep.isBuiltin() == null || !proposedPolicyRep.isBuiltin()) {
throw new ClientPolicyException("ordinal client policy not allowed.");
}
ClientPolicyRepresentation policyRep = new ClientPolicyRepresentation();
policyRep.setName(proposedPolicyRep.getName());
policyRep.setDescription(proposedPolicyRep.getDescription());
policyRep.setBuiltin(Boolean.TRUE);
Boolean enabled = (proposedPolicyRep.isEnable() != null) ? proposedPolicyRep.isEnable() : Boolean.FALSE;
policyRep.setEnable(enabled);
policyRep.setConditions(new ArrayList<>());
if (proposedPolicyRep.getConditions() != null) {
for (Object condition : proposedPolicyRep.getConditions()) {
if (isValidCondition(session, condition) == false) {
throw new ClientPolicyException("the proposed client policy contains the condition with its invalid configuration.");
}
policyRep.getConditions().add(condition);
}
}
Set<String> existingProfileNames = new HashSet<>();
ClientProfilesRepresentation reps = getClientProfilesRepresentation(session, null);
reps.getProfiles().stream().map(profile->profile.getName()).forEach(profileName->existingProfileNames.add(profileName));
policyRep.setProfiles(new ArrayList<>());
if (proposedPolicyRep.getProfiles() != null) {
for (String profileName : proposedPolicyRep.getProfiles()) {
if (existingProfileNames.contains(profileName) == false) {
throw new ClientPolicyException("referring not existing client profile not allowed.");
}
}
proposedPolicyRep.getProfiles().stream().distinct().forEach(profileName->policyRep.getProfiles().add(profileName));
}
updatingPoliciesList.add(policyRep);
}
return updatingPoliciesRep;
}
/**
* convert client policies as representation to json.
* can return null.
*/
public static String convertClientPoliciesRepresentationToJson(ClientPoliciesRepresentation reps) throws ClientPolicyException {
return convertRepresentationToJson(reps);
}
/**
* convert client policies as json to representation.
* not return null.
*/
private static ClientPoliciesRepresentation convertClientPoliciesJsonToRepresentation(String json) throws ClientPolicyException {
return convertJsonToRepresentation(json, ClientPoliciesRepresentation.class);
}
/**
* get validated and modified client policies as json.
* it can be constructed by merging proposed client policies with existing client policies.
* can return null.
*/
public static String getValidatedClientPoliciesJson(KeycloakSession session, RealmModel realm, ClientPoliciesRepresentation proposedPoliciesRep) throws ClientPolicyException {
return convertClientPoliciesRepresentationToJson(getValidatedClientPoliciesRepresentation(session, realm, proposedPoliciesRep));
}
/**
* get validated and modified client policies as representation.
* it can be constructed by merging proposed client policies with existing client policies.
* not return null.
*/
private static ClientPoliciesRepresentation getValidatedClientPoliciesRepresentation(KeycloakSession session, RealmModel realm, ClientPoliciesRepresentation proposedPoliciesRep) throws ClientPolicyException {
if (proposedPoliciesRep == null) {
proposedPoliciesRep = new ClientPoliciesRepresentation();
}
if (realm == null) {
throw new ClientPolicyException("realm not specified.");
}
// deserialize existing profiles (json -> represetation)
ClientPoliciesRepresentation existingPoliciesRep = null;
String existingPoliciesJson = session.clientPolicy().getClientPoliciesJsonString(realm);
if (existingPoliciesJson != null) {
existingPoliciesRep = convertClientPoliciesJsonToRepresentation(existingPoliciesJson);
if (existingPoliciesRep == null) {
existingPoliciesRep = new ClientPoliciesRepresentation();
}
} else {
existingPoliciesRep = new ClientPoliciesRepresentation();
}
// no policy contained (it is valid)
// back to initial builtin policies
List<ClientPolicyRepresentation> proposedPolicyRepList = proposedPoliciesRep.getPolicies();
if (proposedPolicyRepList == null || proposedPolicyRepList.isEmpty()) {
proposedPolicyRepList = new ArrayList<>();
proposedPoliciesRep.setPolicies(new ArrayList<>());
}
// duplicated policy name is not allowed.
if (proposedPolicyRepList.size() != proposedPolicyRepList.stream().map(i->i.getName()).distinct().count()) {
throw new ClientPolicyException("proposed client policy name duplicated.");
}
// construct updating policies from existing policies and proposed policies
ClientPoliciesRepresentation updatingPoliciesRep = new ClientPoliciesRepresentation();
updatingPoliciesRep.setPolicies(new ArrayList<>());
List<ClientPolicyRepresentation> updatingPoliciesList = updatingPoliciesRep.getPolicies();
// add existing builtin policies to updating policies
List<ClientPolicyRepresentation> existingPoliciesList = existingPoliciesRep.getPolicies();
if (existingPoliciesList != null && !existingPoliciesList.isEmpty()) {
existingPoliciesList.stream().filter(i->i.isBuiltin()).forEach(i->updatingPoliciesList.add(i));
}
for (ClientPolicyRepresentation proposedPolicyRep : proposedPoliciesRep.getPolicies()) {
if (proposedPolicyRep.getName() == null) {
throw new ClientPolicyException("proposed client policy name missing.");
}
// newly proposed builtin policy not allowed because builtin policy cannot added/deleted/modified.
Boolean enabled = (proposedPolicyRep.isEnable() != null) ? proposedPolicyRep.isEnable() : Boolean.FALSE;
if (proposedPolicyRep.isBuiltin() != null && proposedPolicyRep.isBuiltin()) {
// only enable field of the existing builtin policy can be overridden.
if (updatingPoliciesList.stream().anyMatch(i->i.getName().equals(proposedPolicyRep.getName()))) {
updatingPoliciesList.stream().filter(i->i.getName().equals(proposedPolicyRep.getName())).forEach(i->i.setEnable(enabled));
continue;
}
throw new ClientPolicyException("newly builtin proposed client policy not allowed.");
}
// basically, proposed policy totally overrides existing policy except for enabled field..
ClientPolicyRepresentation policyRep = new ClientPolicyRepresentation();
policyRep.setName(proposedPolicyRep.getName());
policyRep.setDescription(proposedPolicyRep.getDescription());
policyRep.setBuiltin(Boolean.FALSE);
policyRep.setEnable(enabled);
policyRep.setConditions(new ArrayList<>());
if (proposedPolicyRep.getConditions() != null) {
for (Object condition : proposedPolicyRep.getConditions()) {
if (isValidCondition(session, condition) == false) {
throw new ClientPolicyException("the proposed client policy contains the condition with its invalid configuration.");
}
policyRep.getConditions().add(condition);
}
}
Set<String> existingProfileNames = new HashSet<>();
ClientProfilesRepresentation reps = getClientProfilesRepresentation(session, realm);
if (reps.getProfiles() != null) {
reps.getProfiles().stream().map(profile->profile.getName()).forEach(profileName->existingProfileNames.add(profileName));
}
policyRep.setProfiles(new ArrayList<>());
if (proposedPolicyRep.getProfiles() != null) {
for (String profileName : proposedPolicyRep.getProfiles()) {
if (existingProfileNames.contains(profileName) == false) {
throw new ClientPolicyException("referring not existing client profile not allowed.");
}
}
proposedPolicyRep.getProfiles().stream().distinct().forEach(profileName->policyRep.getProfiles().add(profileName));
}
updatingPoliciesList.add(policyRep);
}
return updatingPoliciesRep;
}
/**
* get validated and modified builtin client policies in a realm as representation.
* it can be constructed by merging proposed client policies with existing client policies.
* not return null.
*/
public static ClientPoliciesRepresentation getValidatedClientPoliciesRepresentation(KeycloakSession session, RealmModel realm, String policiesJson) throws ClientPolicyException {
if (policiesJson == null) {
throw new ClientPolicyException("no client policies json.");
}
// deserialize existing policies (json -> representation)
ClientPoliciesRepresentation proposedPoliciesRep = convertClientPoliciesJsonToRepresentation(policiesJson);
return getValidatedClientPoliciesRepresentation(session, realm, proposedPoliciesRep);
}
/**
* check whether the proposed condition's provider can be found in keycloak's ClientPolicyConditionProvider list.
* not return null.
*/
private static boolean isValidCondition(KeycloakSession session, Object condition) {
return isValidComponent(session, condition, "condition", (String providerId) -> {
Set<String> providerSet = session.listProviderIds(ClientPolicyConditionProvider.class);
if (providerSet != null && providerSet.contains(providerId)) {
return true;
}
logger.warnv("no executor provider found. providerId = {0}", providerId);
return false;
});
}
private static boolean isValidComponent(KeycloakSession session, Object obj, String type, Predicate<String> f) {
JsonNode node = null;
try {
node = objectMapper.convertValue(obj, JsonNode.class);
} catch (IllegalArgumentException iae) {
logger.warnv("invalid json string representating {0}. err={1}", type, iae.getMessage());
return false;
}
Iterator<Entry<String, JsonNode>> it = node.fields();
while (it.hasNext()) {
Entry<String, JsonNode> entry = it.next();
// whether find provider
if(!f.test(entry.getKey())) return false;
}
return true;
}
private static String convertRepresentationToJson(Object reps) throws ClientPolicyException {
if (reps == null) return null;
String json = null;
try {
json = objectMapper.writeValueAsString(reps);
} catch (JsonProcessingException jpe) {
throw new ClientPolicyException(jpe.getMessage());
}
return json;
}
private static <T> T convertJsonToRepresentation(String json, Class<T> type) throws ClientPolicyException {
if (json == null) {
throw new ClientPolicyException("no json.");
}
T rep = null;
try {
rep = JsonSerialization.readValue(json, type);
} catch (IOException ioe) {
throw new ClientPolicyException("failed to deserialize.", ioe.getMessage());
}
return rep;
}
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright 2020 Red Hat, Inc. and/or its affiliates * Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags. * and other contributors as indicated by the @author tags.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -16,15 +16,12 @@
* *
*/ */
package org.keycloak.services.clientpolicy.executor; package org.keycloak.services.clientpolicy;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.keycloak.provider.ProviderFactory;
/** /**
* Just adds some type-safety to the ClientPolicyExecutorConfiguration
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) public interface ClientPolicyManagerFactory extends ProviderFactory<ClientPolicyManager> {
public class ClientPolicyExecutorConfiguration {
} }

View file

@ -0,0 +1,49 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.services.clientpolicy;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ClientPolicyManagerSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "client-policy-manager";
}
@Override
public Class<? extends Provider> getProviderClass() {
return ClientPolicyManager.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return ClientPolicyManagerFactory.class;
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.services.clientpolicy.condition;
import java.util.Optional;
import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public abstract class AbstractClientPolicyConditionProvider<CONFIG extends ClientPolicyConditionConfigurationRepresentation> implements ClientPolicyConditionProvider<CONFIG> {
protected final KeycloakSession session;
protected CONFIG configuration;
public AbstractClientPolicyConditionProvider(KeycloakSession session) {
this.session = session;
}
@Override
public void setupConfiguration(CONFIG config) {
if (config == null) {
// Fallback for the case that null configuration is passed as an argument
this.configuration = JsonSerialization.mapper.convertValue(new ClientPolicyConditionConfigurationRepresentation(), getConditionConfigurationClass());
} else {
this.configuration = config;
}
}
public boolean isNegativeLogic() throws ClientPolicyException {
if (configuration == null) {
throw new ClientPolicyException("Not allowed to call this when configuration is not set");
}
return Optional.ofNullable(this.configuration.isNegativeLogic()).orElse(Boolean.FALSE).booleanValue();
}
}

View file

@ -1,30 +0,0 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.services.clientpolicy.condition;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* Just adds some type-safety to the ClientPolicyConditionConfiguration
*
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class ClientPolicyConditionConfiguration {
}

View file

@ -18,6 +18,7 @@
package org.keycloak.services.clientpolicy.condition; package org.keycloak.services.clientpolicy.condition;
import org.keycloak.provider.Provider; import org.keycloak.provider.Provider;
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyEvent; import org.keycloak.services.clientpolicy.ClientPolicyEvent;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
@ -31,7 +32,7 @@ import org.keycloak.services.clientpolicy.ClientPolicyVote;
* *
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public interface ClientPolicyConditionProvider<CONFIG extends ClientPolicyConditionConfiguration> extends Provider { public interface ClientPolicyConditionProvider<CONFIG extends ClientPolicyConditionConfigurationRepresentation> extends Provider {
@Override @Override
default void close() { default void close() {
@ -42,14 +43,13 @@ public interface ClientPolicyConditionProvider<CONFIG extends ClientPolicyCondit
* *
* @param config * @param config
*/ */
default void setupConfiguration(CONFIG config) { void setupConfiguration(CONFIG config);
}
/** /**
* @return Class, which should match the "config" argument of the {@link #setupConfiguration(ClientPolicyConditionConfiguration)} * @return Class, which should match the "config" argument of the {@link #setupConfiguration(ClientPolicyConditionConfigurationRepresentation)}
*/ */
default Class<CONFIG> getConditionConfigurationClass() { default Class<CONFIG> getConditionConfigurationClass() {
return (Class<CONFIG>) ClientPolicyConditionConfiguration.class; return (Class<CONFIG>) ClientPolicyConditionConfigurationRepresentation.class;
} }
/** /**
@ -73,9 +73,7 @@ public interface ClientPolicyConditionProvider<CONFIG extends ClientPolicyCondit
* *
* @return true if the result of applyPolicy method is inverted. * @return true if the result of applyPolicy method is inverted.
*/ */
default boolean isNegativeLogic() { boolean isNegativeLogic() throws ClientPolicyException;
return false;
}
default String getName() { default String getName() {
return getClass().toString(); return getClass().toString();

View file

@ -17,11 +17,18 @@
package org.keycloak.services.clientpolicy.condition; package org.keycloak.services.clientpolicy.condition;
import org.keycloak.common.Profile;
import org.keycloak.provider.ConfiguredProvider; import org.keycloak.provider.ConfiguredProvider;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.ProviderFactory;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public interface ClientPolicyConditionProviderFactory extends ProviderFactory<ClientPolicyConditionProvider>, ConfiguredProvider { public interface ClientPolicyConditionProviderFactory extends ProviderFactory<ClientPolicyConditionProvider>, ConfiguredProvider, EnvironmentDependentProviderFactory {
@Override
default boolean isSupported() {
return Profile.isFeatureEnabled(Profile.Feature.CLIENT_POLICIES);
}
} }

View file

@ -18,6 +18,7 @@
package org.keycloak.services.clientpolicy.executor; package org.keycloak.services.clientpolicy.executor;
import org.keycloak.provider.Provider; import org.keycloak.provider.Provider;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyEvent; import org.keycloak.services.clientpolicy.ClientPolicyEvent;
@ -30,7 +31,7 @@ import org.keycloak.services.clientpolicy.ClientPolicyEvent;
* *
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public interface ClientPolicyExecutorProvider<CONFIG extends ClientPolicyExecutorConfiguration> extends Provider { public interface ClientPolicyExecutorProvider<CONFIG extends ClientPolicyExecutorConfigurationRepresentation> extends Provider {
@Override @Override
default void close() { default void close() {
@ -45,10 +46,10 @@ public interface ClientPolicyExecutorProvider<CONFIG extends ClientPolicyExecuto
} }
/** /**
* @return Class, which should match the "config" argument of the {@link #setupConfiguration(ClientPolicyExecutorConfiguration)} * @return Class, which should match the "config" argument of the {@link #setupConfiguration(ClientPolicyExecutorConfigurationRepresentation)}
*/ */
default Class<CONFIG> getExecutorConfigurationClass() { default Class<CONFIG> getExecutorConfigurationClass() {
return (Class<CONFIG>) ClientPolicyExecutorConfiguration.class; return (Class<CONFIG>) ClientPolicyExecutorConfigurationRepresentation.class;
} }
/** /**

View file

@ -17,11 +17,18 @@
package org.keycloak.services.clientpolicy.executor; package org.keycloak.services.clientpolicy.executor;
import org.keycloak.common.Profile;
import org.keycloak.provider.ConfiguredProvider; import org.keycloak.provider.ConfiguredProvider;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.ProviderFactory;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public interface ClientPolicyExecutorProviderFactory extends ProviderFactory<ClientPolicyExecutorProvider>, ConfiguredProvider { public interface ClientPolicyExecutorProviderFactory extends ProviderFactory<ClientPolicyExecutorProvider>, ConfiguredProvider, EnvironmentDependentProviderFactory {
@Override
default boolean isSupported() {
return Profile.isFeatureEnabled(Profile.Feature.CLIENT_POLICIES);
}
} }

View file

@ -97,4 +97,5 @@ org.keycloak.validation.ClientValidationSPI
org.keycloak.headers.SecurityHeadersSpi org.keycloak.headers.SecurityHeadersSpi
org.keycloak.services.clientpolicy.condition.ClientPolicyConditionSpi org.keycloak.services.clientpolicy.condition.ClientPolicyConditionSpi
org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorSpi org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorSpi
org.keycloak.services.clientpolicy.ClientPolicyManagerSpi
org.keycloak.userprofile.UserProfileSpi org.keycloak.userprofile.UserProfileSpi

View file

@ -18,6 +18,9 @@
package org.keycloak.services.clientpolicy; package org.keycloak.services.clientpolicy;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.provider.Provider;
import org.keycloak.representations.idm.ClientPoliciesRepresentation;
import org.keycloak.representations.idm.ClientProfilesRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
/** /**
@ -26,7 +29,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
* *
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public interface ClientPolicyManager { public interface ClientPolicyManager extends Provider {
/** /**
* execute a method for handling an event defined in {@link ClientPolicyEvent}. * execute a method for handling an event defined in {@link ClientPolicyEvent}.
@ -37,18 +40,7 @@ public interface ClientPolicyManager {
void triggerOnEvent(ClientPolicyContext context) throws ClientPolicyException; void triggerOnEvent(ClientPolicyContext context) throws ClientPolicyException;
/** /**
* when booting keycloak, reads json representations of the builtin client profiles and policies from files * when creating a realm, adds the default client policies, which should be available on the realm and put them onto the realm as its attribute.
* enclosed in keycloak-services jar file and put them onto the keycloak application.
* if these operation fails, put null.
*
* @param profilesFilePath - the file path for the builtin client profiles
* @param policiesFilePath - the file path for the builtin client policies
*/
void setupClientPoliciesOnKeycloakApp(String profilesFilePath, String policiesFilePath);
/**
* when creating a realm, reads the builtin client profiles and policies
* that have already been set on keycloak application on booting keycloak and put them onto the realm as its attribute.
* if these operation fails, put null. * if these operation fails, put null.
* *
* @param realm - the newly created realm * @param realm - the newly created realm
@ -56,34 +48,35 @@ public interface ClientPolicyManager {
void setupClientPoliciesOnCreatedRealm(RealmModel realm); void setupClientPoliciesOnCreatedRealm(RealmModel realm);
/** /**
* when importing a realm, reads the builtin client profiles and policies * when importing a realm, or updating a realm, update model from the representation object
* that have already been set on keycloak application on booting keycloak and override them
* with ones loaded from the imported realm json file.
* if these operation fails, rolls them back to the builtin client profiles and policies set on keycloak application.
* *
* @param realm - the newly created realm to be overriden by imported realm's representation * @param realm - the newly created realm to be overriden by imported realm's representation
* @param rep - imported realm's representation * @param rep - imported realm's representation
*/ */
void setupClientPoliciesOnImportedRealm(RealmModel realm, RealmRepresentation rep); void updateRealmModelFromRepresentation(RealmModel realm, RealmRepresentation rep);
/** /**
* when updating client profiles via Admin REST API, reads the json representation of the client profiles * when updating client profiles via Admin REST API, reads the json representation of the client profiles
* and overrides the existing client profiles set on the realm with them. * and overrides the existing client profiles set on the realm with them.
* if these operation fails, rolls them back to the existing client profiles and throw an exception. * if these operation fails, rolls them back to the existing client profiles and throw an exception.
* *
* If the "clientProfiles" parameter contains the global client profiles, they won't be updated on the realm at all
*
* @param realm - the realm whose client profiles is to be overriden by the new client profiles * @param realm - the realm whose client profiles is to be overriden by the new client profiles
* @param json - the json representation of the new client profiles that overrides the existing client profiles set on the realm * @param clientProfiles - the json representation of the new client profiles that overrides the existing client profiles set on the realm. With
* the exception of global profiles, which are not overriden as mentioned above.
* @throws {@link ClientPolicyException} * @throws {@link ClientPolicyException}
*/ */
void updateClientProfiles(RealmModel realm, String json) throws ClientPolicyException; void updateClientProfiles(RealmModel realm, ClientProfilesRepresentation clientProfiles) throws ClientPolicyException;
/** /**
* when getting client profiles via Admin REST API, returns the existing client profiles set on the realm. * when getting client profiles via Admin REST API, returns the existing client profiles set on the realm.
* *
* @param realm - the realm whose client profiles is to be returned * @param realm - the realm whose client profiles is to be returned
* @param includeGlobalProfiles - If true, method will return realm profiles and global profiles as well. If false, then "globalProfiles" field would be null
* @return the json representation of the client profiles set on the realm * @return the json representation of the client profiles set on the realm
*/ */
String getClientProfiles(RealmModel realm); ClientProfilesRepresentation getClientProfiles(RealmModel realm, boolean includeGlobalProfiles) throws ClientPolicyException;
/** /**
* when updating client policies via Admin REST API, reads the json representation of the client policies * when updating client policies via Admin REST API, reads the json representation of the client policies
@ -91,10 +84,10 @@ public interface ClientPolicyManager {
* if these operation fails, rolls them back to the existing client policies and throw an exception. * if these operation fails, rolls them back to the existing client policies and throw an exception.
* *
* @param realm - the realm whose client policies is to be overriden by the new client policies * @param realm - the realm whose client policies is to be overriden by the new client policies
* @param json - the json representation of the new client policies that overrides the existing client policies set on the realm * @param clientPolicies - the json representation of the new client policies that overrides the existing client policies set on the realm
* @throws {@link ClientPolicyException} * @throws {@link ClientPolicyException}
*/ */
void updateClientPolicies(RealmModel realm, String json) throws ClientPolicyException; void updateClientPolicies(RealmModel realm, ClientPoliciesRepresentation clientPolicies) throws ClientPolicyException;
/** /**
* when getting client policies via Admin REST API, returns the existing client policies set on the realm. * when getting client policies via Admin REST API, returns the existing client policies set on the realm.
@ -102,45 +95,15 @@ public interface ClientPolicyManager {
* @param realm - the realm whose client policies is to be returned * @param realm - the realm whose client policies is to be returned
* @return the json representation of the client policies set on the realm * @return the json representation of the client policies set on the realm
*/ */
String getClientPolicies(RealmModel realm); ClientPoliciesRepresentation getClientPolicies(RealmModel realm) throws ClientPolicyException;
/** /**
* when exporting realm the realm, prepares the exported representation of the client profiles and policies. * when exporting realm, or retrieve the realm for admin REST API, prepares the exported representation of the client profiles and policies.
* E.g. the builtin client profiles and policies are filtered out and not exported. * Global client profiles and policies are filtered out and not exported.
* *
* @param realm - the realm to be exported * @param realm - the realm to be exported
* @param rep - the realm's representation to be exported actually * @param rep - the realm's representation to be exported actually
*/ */
void setupClientPoliciesOnExportingRealm(RealmModel realm, RealmRepresentation rep); void updateRealmRepresentationFromModel(RealmModel realm, RealmRepresentation rep);
/**
* returns the json representation of the builtin client profiles set on keycloak application.
*
* @return the json representation of the builtin client profiles set on keycloak application
*/
String getClientProfilesOnKeycloakApp();
/**
* returns the json representation of the builtin client policies set on keycloak application.
*
* @return the json representation of the builtin client policies set on keycloak application
*/
String getClientPoliciesOnKeycloakApp();
/**
* returns the json representation of the client profiles set on the realm.
*
* @param realm - the realm whose client profiles is to be returned
* @return the json representation of the client profiles set on the realm
*/
String getClientProfilesJsonString(RealmModel realm);
/**
* returns the json representation of the client policies set on the realm.
*
* @param realm - the realm whose client policies is to be returned
* @return the json representation of the client policies set on the realm
*/
String getClientPoliciesJsonString(RealmModel realm);
} }

View file

@ -87,7 +87,7 @@ public class ExportUtils {
} }
public static RealmRepresentation exportRealm(KeycloakSession session, RealmModel realm, ExportOptions options, boolean internal) { public static RealmRepresentation exportRealm(KeycloakSession session, RealmModel realm, ExportOptions options, boolean internal) {
RealmRepresentation rep = ModelToRepresentation.toRepresentation(realm, internal); RealmRepresentation rep = ModelToRepresentation.toRepresentation(session, realm, internal);
ModelToRepresentation.exportAuthenticationFlows(realm, rep); ModelToRepresentation.exportAuthenticationFlows(realm, rep);
ModelToRepresentation.exportRequiredActions(realm, rep); ModelToRepresentation.exportRequiredActions(realm, rep);
@ -260,9 +260,6 @@ public class ExportUtils {
MultivaluedHashMap<String, ComponentExportRepresentation> components = exportComponents(realm, realm.getId()); MultivaluedHashMap<String, ComponentExportRepresentation> components = exportComponents(realm, realm.getId());
rep.setComponents(components); rep.setComponents(components);
// client policies
session.clientPolicy().setupClientPoliciesOnExportingRealm(realm, rep);
return rep; return rep;
} }

View file

@ -499,7 +499,7 @@ public class DefaultKeycloakSession implements KeycloakSession {
@Override @Override
public ClientPolicyManager clientPolicy() { public ClientPolicyManager clientPolicy() {
if (clientPolicyManager == null) { if (clientPolicyManager == null) {
clientPolicyManager = new DefaultClientPolicyManager(this); clientPolicyManager = getProvider(ClientPolicyManager.class);
} }
return clientPolicyManager; return clientPolicyManager;
} }

View file

@ -0,0 +1,514 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.services.clientpolicy;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.jboss.logging.Logger;
import org.keycloak.common.Profile;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.ClientPoliciesRepresentation;
import org.keycloak.representations.idm.ClientPolicyConditionRepresentation;
import org.keycloak.representations.idm.ClientPolicyExecutorRepresentation;
import org.keycloak.representations.idm.ClientPolicyRepresentation;
import org.keycloak.representations.idm.ClientProfileRepresentation;
import org.keycloak.representations.idm.ClientProfilesRepresentation;
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
import org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProvider;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider;
import org.keycloak.util.JsonSerialization;
/**
* Utilities for treating client policies/profiles
*
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/
public class ClientPoliciesUtil {
private static final Logger logger = Logger.getLogger(ClientPoliciesUtil.class);
/**
* gets existing client profiles in a realm as representation.
* not return null.
*/
static ClientProfilesRepresentation getClientProfilesRepresentation(KeycloakSession session, RealmModel realm) throws ClientPolicyException {
String profilesJson = getClientProfilesJsonString(realm);
// deserialize existing profiles (json -> representation)
if (profilesJson == null) {
return new ClientProfilesRepresentation();
}
return convertClientProfilesJsonToRepresentation(profilesJson);
}
/**
* gets existing client profiles in a realm as model.
* not return null.
*/
static Map<String, ClientProfileModel> getClientProfilesModel(KeycloakSession session, RealmModel realm, List<ClientProfileRepresentation> globalClientProfiles) {
// get existing profiles as json
String profilesJson = getClientProfilesJsonString(realm);
if (profilesJson == null) {
return Collections.emptyMap();
}
// deserialize existing profiles (json -> representation)
ClientProfilesRepresentation profilesRep = null;
try {
profilesRep = convertClientProfilesJsonToRepresentation(profilesJson);
} catch (ClientPolicyException e) {
logger.warnv("Failed to serialize client profiles json string. err={0}, errDetail={1}", e.getError(), e.getErrorDetail());
return Collections.emptyMap();
}
if (profilesRep == null || profilesRep.getProfiles() == null) {
return Collections.emptyMap();
}
List<ClientProfileRepresentation> profiles = profilesRep.getProfiles();
// Add global profiles as well
profiles.addAll(globalClientProfiles);
// constructing existing profiles (representation -> model)
Map<String, ClientProfileModel> profileMap = new HashMap<>();
for (ClientProfileRepresentation profileRep : profilesRep.getProfiles()) {
// ignore profile without name
if (profileRep.getName() == null) {
continue;
}
ClientProfileModel profileModel = new ClientProfileModel();
profileModel.setName(profileRep.getName());
profileModel.setDescription(profileRep.getDescription());
if (profileRep.getExecutors() == null) {
profileModel.setExecutors(new ArrayList<>());
profileMap.put(profileRep.getName(), profileModel);
continue;
}
List<ClientPolicyExecutorProvider> executors = new ArrayList<>();
if (profileRep.getExecutors() != null) {
for (ClientPolicyExecutorRepresentation executorRep : profileRep.getExecutors()) {
ClientPolicyExecutorProvider provider = session.getProvider(ClientPolicyExecutorProvider.class, executorRep.getExecutorProviderId());
if (provider == null) {
// executor's provider not found. just skip it.
logger.warnf("Executor with provider ID %s not found", executorRep.getExecutorProviderId());
continue;
}
try {
ClientPolicyExecutorConfigurationRepresentation configuration = (ClientPolicyExecutorConfigurationRepresentation) JsonSerialization.mapper.convertValue(executorRep.getConfiguration(), provider.getExecutorConfigurationClass());
provider.setupConfiguration(configuration);
executors.add(provider);
} catch (IllegalArgumentException iae) {
logger.warnv("failed for Configuration Setup during setup provider {0} :: error = {1}", executorRep.getExecutorProviderId(), iae.getMessage());
}
}
}
profileModel.setExecutors(executors);
profileMap.put(profileRep.getName(), profileModel);
}
return profileMap;
}
/**
* get validated and modified global (built-in) client profiles set on keycloak app as representation.
* it is loaded from json file enclosed in keycloak's binary.
* not return null.
*/
static List<ClientProfileRepresentation> getValidatedGlobalClientProfilesRepresentation(KeycloakSession session, InputStream is) throws ClientPolicyException {
// load builtin client profiles representation
ClientProfilesRepresentation proposedProfilesRep = null;
try {
proposedProfilesRep = JsonSerialization.readValue(is, ClientProfilesRepresentation.class);
} catch (Exception e) {
throw new ClientPolicyException("failed to deserialize global proposed client profiles json string.", e.getMessage());
}
if (proposedProfilesRep == null) {
return Collections.emptyList();
}
// no profile contained (it is valid)
List<ClientProfileRepresentation> proposedProfileRepList = proposedProfilesRep.getProfiles();
if (proposedProfileRepList == null || proposedProfileRepList.isEmpty()) {
return Collections.emptyList();
}
// duplicated profile name is not allowed.
if (proposedProfileRepList.size() != proposedProfileRepList.stream().map(i->i.getName()).distinct().count()) {
throw new ClientPolicyException("proposed global client profile name duplicated.");
}
// construct validated and modified profiles from builtin profiles in JSON file enclosed in keycloak binary.
List<ClientProfileRepresentation> updatingProfileList = new LinkedList<>();
for (ClientProfileRepresentation proposedProfileRep : proposedProfilesRep.getProfiles()) {
if (proposedProfileRep.getName() == null) {
throw new ClientPolicyException("client profile without its name not allowed.");
}
ClientProfileRepresentation profileRep = new ClientProfileRepresentation();
profileRep.setName(proposedProfileRep.getName());
profileRep.setDescription(proposedProfileRep.getDescription());
profileRep.setExecutors(new ArrayList<>()); // to prevent returning null
if (proposedProfileRep.getExecutors() != null) {
for (ClientPolicyExecutorRepresentation executorRep : proposedProfileRep.getExecutors()) {
// Skip the check if feature is disabled as then the executor implementations are disabled
if (Profile.isFeatureEnabled(Profile.Feature.CLIENT_POLICIES) && !isValidExecutor(session, executorRep.getExecutorProviderId())) {
throw new ClientPolicyException("proposed client profile contains the executor with its invalid configuration.");
}
profileRep.getExecutors().add(executorRep);
}
}
updatingProfileList.add(profileRep);
}
return updatingProfileList;
}
/**
* convert client profiles as representation to json.
* can return null.
*/
public static String convertClientProfilesRepresentationToJson(ClientProfilesRepresentation reps) throws ClientPolicyException {
try {
return JsonSerialization.writeValueAsString(reps);
} catch (IOException ioe) {
throw new ClientPolicyException(ioe.getMessage());
}
}
/**
* convert client profiles as json to representation.
* not return null.
*/
private static ClientProfilesRepresentation convertClientProfilesJsonToRepresentation(String json) throws ClientPolicyException {
try {
return JsonSerialization.readValue(json, ClientProfilesRepresentation.class);
} catch (IOException ioe) {
throw new ClientPolicyException(ioe.getMessage());
}
}
/**
* get validated and modified client profiles as representation.
* it can be constructed by merging proposed client profiles with existing client profiles.
* not return null.
*/
static ClientProfilesRepresentation getValidatedClientProfilesForUpdate(KeycloakSession session, RealmModel realm,
ClientProfilesRepresentation proposedProfilesRep, List<ClientProfileRepresentation> globalClientProfiles) throws ClientPolicyException {
if (realm == null) {
throw new ClientPolicyException("realm not specified.");
}
// no profile contained (it is valid)
List<ClientProfileRepresentation> proposedProfileRepList = proposedProfilesRep.getProfiles();
if (proposedProfileRepList == null || proposedProfileRepList.isEmpty()) {
proposedProfileRepList = new ArrayList<>();
proposedProfilesRep.setProfiles(new ArrayList<>());
}
// Profile without name not allowed
if (proposedProfileRepList.stream().anyMatch(clientProfile -> clientProfile.getName() == null || clientProfile.getName().isEmpty())) {
throw new ClientPolicyException("client profile without its name not allowed.");
}
// duplicated profile name is not allowed.
if (proposedProfileRepList.size() != proposedProfileRepList.stream().map(i->i.getName()).distinct().count()) {
throw new ClientPolicyException("proposed client profile name duplicated.");
}
// Conflict with any global profile is not allowed
Set<String> globalProfileNames = globalClientProfiles.stream().map(ClientProfileRepresentation::getName).collect(Collectors.toSet());
for (ClientProfileRepresentation clientProfile : proposedProfileRepList) {
if (globalProfileNames.contains(clientProfile.getName())) {
throw new ClientPolicyException("Proposed profile name duplicated as the name of some global profile");
}
}
// Validate executor
for (ClientProfileRepresentation proposedProfileRep : proposedProfilesRep.getProfiles()) {
if (proposedProfileRep.getExecutors() != null) {
for (ClientPolicyExecutorRepresentation executorRep : proposedProfileRep.getExecutors()) {
if (!isValidExecutor(session, executorRep.getExecutorProviderId())) {
throw new ClientPolicyException("proposed client profile contains the executor, which does not have valid provider, or has invalid configuration.");
}
}
}
}
// Make sure to not save built-in inside realm attribute
proposedProfilesRep.setGlobalProfiles(null);
return proposedProfilesRep;
}
/**
* check whether the proposed executor's provider can be found in keycloak's ClientPolicyExecutorProvider list.
* not return null.
*/
private static boolean isValidExecutor(KeycloakSession session, String executorProviderId) {
Set<String> providerSet = session.listProviderIds(ClientPolicyExecutorProvider.class);
if (providerSet != null && providerSet.contains(executorProviderId)) {
return true;
}
logger.warnv("no executor provider found. providerId = {0}", executorProviderId);
return false;
}
/**
* get existing client policies in a realm as representation.
* not return null.
*/
static ClientPoliciesRepresentation getClientPoliciesRepresentation(KeycloakSession session, RealmModel realm) throws ClientPolicyException {
// get existing policies json
String policiesJson = getClientPoliciesJsonString(realm);
// deserialize existing policies (json -> representation)
if (policiesJson == null) {
return new ClientPoliciesRepresentation();
}
return convertClientPoliciesJsonToRepresentation(policiesJson);
}
/**
* get existing enabled client policies in a realm as model.
* not return null.
*/
static List<ClientPolicyModel> getEnabledClientPoliciesModel(KeycloakSession session, RealmModel realm) {
// get existing profiles as json
String policiesJson = getClientPoliciesJsonString(realm);
if (policiesJson == null) {
return Collections.emptyList();
}
// deserialize existing policies (json -> representation)
ClientPoliciesRepresentation policiesRep = null;
try {
policiesRep = convertClientPoliciesJsonToRepresentation(policiesJson);
} catch (ClientPolicyException e) {
logger.warnv("Failed to serialize client policies json string. err={0}, errDetail={1}", e.getError(), e.getErrorDetail());
return Collections.emptyList();
}
if (policiesRep == null || policiesRep.getPolicies() == null) {
return Collections.emptyList();
}
// constructing existing policies (representation -> model)
List<ClientPolicyModel> policyList = new ArrayList<>();
for (ClientPolicyRepresentation policyRep: policiesRep.getPolicies()) {
// ignore policy without name
if (policyRep.getName() == null) {
logger.warnf("Ignored client policy without name in the realm %s", realm.getName());
continue;
}
// pick up only enabled policy
if (policyRep.isEnabled() == null || policyRep.isEnabled() == false) {
continue;
}
ClientPolicyModel policyModel = new ClientPolicyModel();
policyModel.setName(policyRep.getName());
policyModel.setDescription(policyRep.getDescription());
policyModel.setEnable(true);
List<ClientPolicyConditionProvider> conditions = new ArrayList<>();
if (policyRep.getConditions() != null) {
for (ClientPolicyConditionRepresentation conditionRep : policyRep.getConditions()) {
ClientPolicyConditionProvider provider = session.getProvider(ClientPolicyConditionProvider.class, conditionRep.getConditionProviderId());
if (provider == null) {
// condition's provider not found. just skip it.
logger.warnf("Condition with provider ID %s not found", conditionRep.getConditionProviderId());
continue;
}
try {
ClientPolicyConditionConfigurationRepresentation configuration = (ClientPolicyConditionConfigurationRepresentation) JsonSerialization.mapper.convertValue(conditionRep.getConfiguration(), provider.getConditionConfigurationClass());
provider.setupConfiguration(configuration);
conditions.add(provider);
} catch (IllegalArgumentException iae) {
logger.warnv("failed for Configuration Setup :: error = {0}", iae.getMessage());
}
}
}
policyModel.setConditions(conditions);
if (policyRep.getProfiles() != null) {
policyModel.setProfiles(policyRep.getProfiles().stream().collect(Collectors.toList()));
}
policyList.add(policyModel);
}
return policyList;
}
/**
* convert client policies as representation to json.
* can return null.
*/
public static String convertClientPoliciesRepresentationToJson(ClientPoliciesRepresentation reps) throws ClientPolicyException {
try {
return JsonSerialization.writeValueAsString(reps);
} catch (IOException ioe) {
throw new ClientPolicyException(ioe.getMessage());
}
}
/**
* convert client policies as json to representation.
* not return null.
*/
private static ClientPoliciesRepresentation convertClientPoliciesJsonToRepresentation(String json) throws ClientPolicyException {
try {
return JsonSerialization.readValue(json, ClientPoliciesRepresentation.class);
} catch (IOException ioe) {
throw new ClientPolicyException(ioe.getMessage());
}
}
/**
* get validated and modified client policies as representation.
* it can be constructed by merging proposed client policies with existing client policies.
* not return null.
*
* @param session
* @param realm
* @param proposedPoliciesRep
*/
static ClientPoliciesRepresentation getValidatedClientPoliciesForUpdate(KeycloakSession session, RealmModel realm,
ClientPoliciesRepresentation proposedPoliciesRep, List<ClientProfileRepresentation> existingGlobalProfiles) throws ClientPolicyException {
if (realm == null) {
throw new ClientPolicyException("realm not specified.");
}
// no policy contained (it is valid)
List<ClientPolicyRepresentation> proposedPolicyRepList = proposedPoliciesRep.getPolicies();
if (proposedPolicyRepList == null || proposedPolicyRepList.isEmpty()) {
proposedPolicyRepList = new ArrayList<>();
proposedPoliciesRep.setPolicies(new ArrayList<>());
}
// Policy without name not allowed
if (proposedPolicyRepList.stream().anyMatch(clientPolicy -> clientPolicy.getName() == null || clientPolicy.getName().isEmpty())) {
throw new ClientPolicyException("proposed client policy name missing.");
}
// duplicated policy name is not allowed.
if (proposedPolicyRepList.size() != proposedPolicyRepList.stream().map(i->i.getName()).distinct().count()) {
throw new ClientPolicyException("proposed client policy name duplicated.");
}
// construct updating policies from existing policies and proposed policies
ClientPoliciesRepresentation updatingPoliciesRep = new ClientPoliciesRepresentation();
updatingPoliciesRep.setPolicies(new ArrayList<>());
List<ClientPolicyRepresentation> updatingPoliciesList = updatingPoliciesRep.getPolicies();
for (ClientPolicyRepresentation proposedPolicyRep : proposedPoliciesRep.getPolicies()) {
// newly proposed builtin policy not allowed because builtin policy cannot added/deleted/modified.
Boolean enabled = (proposedPolicyRep.isEnabled() != null) ? proposedPolicyRep.isEnabled() : Boolean.FALSE;
// basically, proposed policy totally overrides existing policy except for enabled field..
ClientPolicyRepresentation policyRep = new ClientPolicyRepresentation();
policyRep.setName(proposedPolicyRep.getName());
policyRep.setDescription(proposedPolicyRep.getDescription());
policyRep.setEnabled(enabled);
policyRep.setConditions(new ArrayList<>());
if (proposedPolicyRep.getConditions() != null) {
for (ClientPolicyConditionRepresentation conditionRep : proposedPolicyRep.getConditions()) {
if (!isValidCondition(session, conditionRep.getConditionProviderId())) {
throw new ClientPolicyException("the proposed client policy contains the condition with its invalid configuration.");
}
policyRep.getConditions().add(conditionRep);
}
}
Set<String> existingProfileNames = existingGlobalProfiles.stream().map(ClientProfileRepresentation::getName).collect(Collectors.toSet());
ClientProfilesRepresentation reps = getClientProfilesRepresentation(session, realm);
policyRep.setProfiles(new ArrayList<>());
if (reps.getProfiles() != null) {
existingProfileNames.addAll(reps.getProfiles().stream()
.map(ClientProfileRepresentation::getName)
.collect(Collectors.toSet()));
}
if (proposedPolicyRep.getProfiles() != null) {
for (String profileName : proposedPolicyRep.getProfiles()) {
if (!existingProfileNames.contains(profileName)) {
logger.warnf("Client policy %s referred not existing profile %s");
throw new ClientPolicyException("referring not existing client profile not allowed.");
}
}
proposedPolicyRep.getProfiles().stream().distinct().forEach(profileName->policyRep.getProfiles().add(profileName));
}
updatingPoliciesList.add(policyRep);
}
return updatingPoliciesRep;
}
/**
* check whether the proposed condition's provider can be found in keycloak's ClientPolicyConditionProvider list.
* not return null.
*/
private static boolean isValidCondition(KeycloakSession session, String conditionProviderId) {
Set<String> providerSet = session.listProviderIds(ClientPolicyConditionProvider.class);
if (providerSet != null && providerSet.contains(conditionProviderId)) {
return true;
}
logger.warnv("no condition provider found. providerId = {0}", conditionProviderId);
return false;
}
static String getClientProfilesJsonString(RealmModel realm) {
return realm.getAttribute(Constants.CLIENT_PROFILES);
}
static String getClientPoliciesJsonString(RealmModel realm) {
return realm.getAttribute(Constants.CLIENT_POLICIES);
}
static void setClientProfilesJsonString(RealmModel realm, String json) {
realm.setAttribute(Constants.CLIENT_PROFILES, json);
}
static void setClientPoliciesJsonString(RealmModel realm, String json) {
realm.setAttribute(Constants.CLIENT_POLICIES, json);
}
}

View file

@ -20,6 +20,8 @@ package org.keycloak.services.clientpolicy;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProvider;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
@ -27,9 +29,8 @@ public class ClientPolicyModel implements Serializable {
protected String name; protected String name;
protected String description; protected String description;
protected boolean builtin;
protected boolean enable; protected boolean enable;
protected List<Object> conditions; // ClientPolicyConditionProvider is not visible so that use Object. protected List<ClientPolicyConditionProvider> conditions;
protected List<String> profiles; protected List<String> profiles;
public String getName() { public String getName() {
@ -48,14 +49,6 @@ public class ClientPolicyModel implements Serializable {
this.description = description; this.description = description;
} }
public boolean isBuiltin() {
return builtin;
}
public void setBuiltin(boolean builtin) {
this.builtin = builtin;
}
public boolean isEnable() { public boolean isEnable() {
return enable; return enable;
} }
@ -64,11 +57,11 @@ public class ClientPolicyModel implements Serializable {
this.enable = enable; this.enable = enable;
} }
public List<Object> getConditions() { public List<ClientPolicyConditionProvider> getConditions() {
return conditions; return conditions;
} }
public void setConditions(List<Object> conditions) { public void setConditions(List<ClientPolicyConditionProvider> conditions) {
this.conditions = conditions; this.conditions = conditions;
} }

View file

@ -13,6 +13,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
*/ */
package org.keycloak.services.clientpolicy; package org.keycloak.services.clientpolicy;
@ -20,6 +21,8 @@ package org.keycloak.services.clientpolicy;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
@ -27,8 +30,7 @@ public class ClientProfileModel implements Serializable {
protected String name; protected String name;
protected String description; protected String description;
protected boolean builtin; protected List<ClientPolicyExecutorProvider> executors;
protected List<Object> executors; // ClientPolicyExecutorProvider is not visible so that use Object.
public String getName() { public String getName() {
return name; return name;
@ -46,19 +48,11 @@ public class ClientProfileModel implements Serializable {
this.description = description; this.description = description;
} }
public boolean isBuiltin() { public List<ClientPolicyExecutorProvider> getExecutors() {
return builtin;
}
public void setBuiltin(boolean builtin) {
this.builtin = builtin;
}
public List<Object> getExecutors() {
return executors; return executors;
} }
public void setExecutors(List<Object> executors) { public void setExecutors(List<ClientPolicyExecutorProvider> executors) {
this.executors = executors; this.executors = executors;
} }
} }

View file

@ -17,8 +17,11 @@
package org.keycloak.services.clientpolicy; package org.keycloak.services.clientpolicy;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -31,6 +34,7 @@ import org.keycloak.representations.idm.ClientProfilesRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProvider; import org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProvider;
import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider; import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider;
import org.keycloak.util.JsonSerialization;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
@ -40,9 +44,11 @@ public class DefaultClientPolicyManager implements ClientPolicyManager {
private static final Logger logger = Logger.getLogger(DefaultClientPolicyManager.class); private static final Logger logger = Logger.getLogger(DefaultClientPolicyManager.class);
private final KeycloakSession session; private final KeycloakSession session;
private final Supplier<List<ClientProfileRepresentation>> globalClientProfilesSupplier;
public DefaultClientPolicyManager(KeycloakSession session) { public DefaultClientPolicyManager(KeycloakSession session, Supplier<List<ClientProfileRepresentation>> globalClientProfilesSupplier) {
this.session = session; this.session = session;
this.globalClientProfilesSupplier = globalClientProfilesSupplier;
} }
@Override @Override
@ -62,8 +68,8 @@ public class DefaultClientPolicyManager implements ClientPolicyManager {
} }
private void doPolicyOperation(ClientConditionOperation condition, ClientExecutorOperation executor, RealmModel realm) throws ClientPolicyException { private void doPolicyOperation(ClientConditionOperation condition, ClientExecutorOperation executor, RealmModel realm) throws ClientPolicyException {
Map<String, ClientProfileModel> map = ClientPoliciesUtil.getClientProfilesModel(session, realm); Map<String, ClientProfileModel> map = ClientPoliciesUtil.getClientProfilesModel(session, realm, globalClientProfilesSupplier.get());
List<ClientPolicyModel> list = ClientPoliciesUtil.getEnabledClientProfilesModel(session, realm).stream().collect(Collectors.toList()); List<ClientPolicyModel> list = ClientPoliciesUtil.getEnabledClientPoliciesModel(session, realm).stream().collect(Collectors.toList());
if (list == null || list.isEmpty()) { if (list == null || list.isEmpty()) {
logger.trace("POLICY OPERATION :: No enabled policy."); logger.trace("POLICY OPERATION :: No enabled policy.");
@ -71,13 +77,13 @@ public class DefaultClientPolicyManager implements ClientPolicyManager {
} }
for (ClientPolicyModel policy: list) { for (ClientPolicyModel policy: list) {
logger.tracev("POLICY OPERATION :: policy name = {0}, isBuiltin = {1}", policy.getName(), policy.isBuiltin()); logger.tracev("POLICY OPERATION :: policy name = {0}", policy.getName());
if (!isSatisfied(policy, condition)) { if (!isSatisfied(policy, condition)) {
logger.tracev("POLICY UNSATISFIED :: policy name = {0}, isBuiltin = {1}", policy.getName(), policy.isBuiltin()); logger.tracev("POLICY UNSATISFIED :: policy name = {0}", policy.getName());
continue; continue;
} }
logger.tracev("POLICY APPLIED :: policy name = {0}, isBuiltin = {1}", policy.getName(), policy.isBuiltin()); logger.tracev("POLICY APPLIED :: policy name = {0}", policy.getName());
execute(policy, executor, map); execute(policy, executor, map);
} }
} }
@ -92,8 +98,7 @@ public class DefaultClientPolicyManager implements ClientPolicyManager {
} }
boolean ret = false; boolean ret = false;
for (Object obj : policy.getConditions()) { for (ClientPolicyConditionProvider condition : policy.getConditions()) {
ClientPolicyConditionProvider condition = (ClientPolicyConditionProvider)obj;
logger.tracev("CONDITION OPERATION :: policy name = {0}, condition name = {1}, provider id = {2}", policy.getName(), condition.getName(), condition.getProviderId()); logger.tracev("CONDITION OPERATION :: policy name = {0}, condition name = {1}, provider id = {2}", policy.getName(), condition.getName(), condition.getProviderId());
try { try {
ClientPolicyVote vote = op.run(condition); ClientPolicyVote vote = op.run(condition);
@ -148,8 +153,7 @@ public class DefaultClientPolicyManager implements ClientPolicyManager {
continue; continue;
} }
for (Object obj : profile.getExecutors()) { for (ClientPolicyExecutorProvider executor : profile.getExecutors()) {
ClientPolicyExecutorProvider executor = (ClientPolicyExecutorProvider)obj;
logger.tracev("EXECUTION :: policy name = {0}, profile name = {1}, executor name = {2}, provider id = {3}", policy.getName(), profileName, executor.getName(), executor.getProviderId()); logger.tracev("EXECUTION :: policy name = {0}, profile name = {1}, executor name = {2}, provider id = {3}", policy.getName(), profileName, executor.getName(), executor.getProviderId());
try { try {
op.run(executor); op.run(executor);
@ -170,236 +174,121 @@ public class DefaultClientPolicyManager implements ClientPolicyManager {
void run(ClientPolicyExecutorProvider executor) throws ClientPolicyException; void run(ClientPolicyExecutorProvider executor) throws ClientPolicyException;
} }
// Client Polices Realm Attributes Keys
public static final String CLIENT_PROFILES = "client-policies.profiles";
public static final String CLIENT_POLICIES = "client-policies.policies";
// builtin profiles and policies are loaded on booting keycloak at once.
// therefore, their representations are kept and remain unchanged.
// these are shared among all realms.
// those can be null to show that no profile/policy exist
private static String builtinClientProfilesJson;
private static String builtinClientPoliciesJson;
@Override
public void setupClientPoliciesOnKeycloakApp(String profilesFilePath, String policiesFilePath) {
logger.trace("LOAD BUILTIN PROFILE POLICIES ON KEYCLOAK");
// client profile can be referred from client policy so that client profile needs to be loaded at first.
// load builtin profiles on keycloak app
ClientProfilesRepresentation validatedProfilesRep = null;
try {
validatedProfilesRep = ClientPoliciesUtil.getValidatedBuiltinClientProfilesRepresentation(session, getClass().getResourceAsStream(profilesFilePath));
} catch (ClientPolicyException cpe) {
logger.warnv("LOAD BUILTIN PROFILES ON KEYCLOAK FAILED :: error = {0}, error detail = {1}", cpe.getError(), cpe.getErrorDetail());
return;
}
String validatedJson = null;
try {
validatedJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(validatedProfilesRep);
} catch (ClientPolicyException cpe) {
logger.warnv("VALIDATE SERIALIZE BUILTIN PROFILES ON KEYCLOAK FAILED :: error = {0}, error detail = {1}", cpe.getError(), cpe.getErrorDetail());
return;
}
builtinClientProfilesJson = validatedJson;
// load builtin policies on keycloak app
ClientPoliciesRepresentation validatedPoliciesRep = null;
try {
validatedPoliciesRep = ClientPoliciesUtil.getValidatedBuiltinClientPoliciesRepresentation(session, getClass().getResourceAsStream(policiesFilePath));
} catch (ClientPolicyException cpe) {
logger.warnv("LOAD BUILTIN POLICIES ON KEYCLOAK FAILED :: error = {0}, error detail = {1}", cpe.getError(), cpe.getErrorDetail());
builtinClientProfilesJson = null;
return;
}
validatedJson = null;
try {
validatedJson = ClientPoliciesUtil.convertClientPoliciesRepresentationToJson(validatedPoliciesRep);
} catch (ClientPolicyException cpe) {
logger.warnv("VALIDATE SERIALIZE BUILTIN POLICIES ON KEYCLOAK FAILED :: error = {0}, error detail = {1}", cpe.getError(), cpe.getErrorDetail());
builtinClientProfilesJson = null;
return;
}
builtinClientPoliciesJson = validatedJson;
}
@Override @Override
public void setupClientPoliciesOnCreatedRealm(RealmModel realm) { public void setupClientPoliciesOnCreatedRealm(RealmModel realm) {
logger.tracev("LOAD BUILTIN PROFILE POLICIES ON CREATED REALM :: realm = {0}", realm.getName()); // For now, not create any create policies on the new realms. Administrator is supposed to add the policies if needed
// put already loaded builtin profiles/policies on keycloak app to newly created realm
setClientProfilesJsonString(realm, builtinClientProfilesJson);
setClientPoliciesJsonString(realm, builtinClientPoliciesJson);
} }
@Override @Override
public void setupClientPoliciesOnImportedRealm(RealmModel realm, RealmRepresentation rep) { public void updateRealmModelFromRepresentation(RealmModel realm, RealmRepresentation rep) {
logger.tracev("LOAD PROFILE POLICIES ON IMPORTED REALM :: realm = {0}", realm.getName()); logger.tracev("LOAD PROFILE POLICIES ON IMPORTED REALM :: realm = {0}", realm.getName());
// put already loaded builtin profiles/policies on keycloak app to newly created realm if (rep.getClientProfiles() != null) {
setClientProfilesJsonString(realm, builtinClientProfilesJson);
setClientPoliciesJsonString(realm, builtinClientPoliciesJson);
// merge imported polices/profiles with builtin policies/profiles
String validatedJson = null;
try { try {
validatedJson = ClientPoliciesUtil.getValidatedClientProfilesJson(session, realm, rep.getClientProfiles()); updateClientProfiles(realm, rep.getClientProfiles());
} catch (ClientPolicyException e) { } catch (ClientPolicyException e) {
logger.warnv("VALIDATE SERIALIZE IMPORTED REALM PROFILES FAILED :: error = {0}, error detail = {1}", e.getError(), e.getErrorDetail()); logger.warnv("VALIDATE SERIALIZE IMPORTED REALM PROFILES FAILED :: error = {0}, error detail = {1}", e.getError(), e.getErrorDetail());
// revert to builtin profiles throw new RuntimeException("Failed to update client profiles", e);
validatedJson = builtinClientProfilesJson; }
} }
setClientProfilesJsonString(realm, validatedJson);
if (rep.getClientPolicies() != null) {
try { try {
validatedJson = ClientPoliciesUtil.getValidatedClientPoliciesJson(session, realm, rep.getClientPolicies()); updateClientPolicies(realm, rep.getClientPolicies());
} catch (ClientPolicyException e) { } catch (ClientPolicyException e) {
logger.warnv("VALIDATE SERIALIZE IMPORTED REALM POLICIES FAILED :: error = {0}, error detail = {1}", e.getError(), e.getErrorDetail()); logger.warnv("VALIDATE SERIALIZE IMPORTED REALM POLICIES FAILED :: error = {0}, error detail = {1}", e.getError(), e.getErrorDetail());
// revert to builtin profiles throw new RuntimeException("Failed to update client policies", e);
validatedJson = builtinClientPoliciesJson; }
} else {
setupClientPoliciesOnCreatedRealm(realm);
} }
setClientPoliciesJsonString(realm, validatedJson);
} }
@Override @Override
public void updateClientProfiles(RealmModel realm, String json) throws ClientPolicyException { public void updateClientProfiles(RealmModel realm, ClientProfilesRepresentation clientProfiles) throws ClientPolicyException {
logger.tracev("UPDATE PROFILES :: realm = {0}, PUT = {1}", realm.getName(), json);
String validatedJsonString = null;
try { try {
validatedJsonString = getValidatedClientProfilesJson(realm, json); if (clientProfiles == null) {
throw new ClientPolicyException("Passing null clientProfiles not allowed");
}
ClientProfilesRepresentation validatedProfilesRep = ClientPoliciesUtil.getValidatedClientProfilesForUpdate(session, realm, clientProfiles, globalClientProfilesSupplier.get());
String validatedJsonString = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(validatedProfilesRep);
ClientPoliciesUtil.setClientProfilesJsonString(realm, validatedJsonString);
logger.tracev("UPDATE PROFILES :: realm = {0}, validated and modified PUT = {1}", realm.getName(), validatedJsonString);
} catch (ClientPolicyException e) { } catch (ClientPolicyException e) {
logger.warnv("VALIDATE SERIALIZE PROFILES FAILED :: error = {0}, error detail = {1}", e.getError(), e.getErrorDetail()); logger.warnv("VALIDATE SERIALIZE PROFILES FAILED :: error = {0}, error detail = {1}", e.getError(), e.getErrorDetail());
throw e; throw e;
} }
setClientProfilesJsonString(realm, validatedJsonString);
logger.tracev("UPDATE PROFILES :: realm = {0}, validated and modified PUT = {1}", realm.getName(), validatedJsonString);
} }
@Override @Override
public String getClientProfiles(RealmModel realm) { public ClientProfilesRepresentation getClientProfiles(RealmModel realm, boolean includeGlobalProfiles) throws ClientPolicyException {
String json = getClientProfilesJsonString(realm); try {
logger.tracev("GET PROFILES :: realm = {0}, GET = {1}", realm.getName(), json); ClientProfilesRepresentation clientProfiles = ClientPoliciesUtil.getClientProfilesRepresentation(session, realm);
return json; if (includeGlobalProfiles) {
clientProfiles.setGlobalProfiles(new LinkedList<>(globalClientProfilesSupplier.get()));
}
if (logger.isTraceEnabled()) {
logger.tracev("GET PROFILES :: realm = {0}, GET = {1}", realm.getName(), JsonSerialization.writeValueAsString(clientProfiles));
}
return clientProfiles;
} catch (ClientPolicyException e) {
logger.warnv("GET CLIENT PROFILES FAILED :: error = {0}, error detail = {1}", e.getError(), e.getErrorDetail());
throw e;
} catch (IOException ioe) {
throw new RuntimeException("Unexpected exception when converting JSON to String", ioe);
}
} }
@Override @Override
public void updateClientPolicies(RealmModel realm, String json) throws ClientPolicyException { public void updateClientPolicies(RealmModel realm, ClientPoliciesRepresentation clientPolicies) throws ClientPolicyException {
logger.tracev("UPDATE POLICIES :: realm = {0}, PUT = {1}", realm.getName(), json);
String validatedJsonString = null; String validatedJsonString = null;
try { try {
validatedJsonString = getValidatedClientPoliciesJson(realm, json); if (clientPolicies == null) {
throw new ClientPolicyException("Passing null clientPolicies not allowed");
}
ClientPoliciesRepresentation clientPoliciesRep = ClientPoliciesUtil.getValidatedClientPoliciesForUpdate(session, realm, clientPolicies, globalClientProfilesSupplier.get());
validatedJsonString = ClientPoliciesUtil.convertClientPoliciesRepresentationToJson(clientPoliciesRep);
} catch (ClientPolicyException e) { } catch (ClientPolicyException e) {
logger.warnv("VALIDATE SERIALIZE POLICIES FAILED :: error = {0}, error detail = {1}", e.getError(), e.getErrorDetail()); logger.warnv("VALIDATE SERIALIZE POLICIES FAILED :: error = {0}, error detail = {1}", e.getError(), e.getErrorDetail());
throw e; throw e;
} }
setClientPoliciesJsonString(realm, validatedJsonString); ClientPoliciesUtil.setClientPoliciesJsonString(realm, validatedJsonString);
logger.tracev("UPDATE POLICIES :: realm = {0}, validated and modified PUT = {1}", realm.getName(), validatedJsonString); logger.tracev("UPDATE POLICIES :: realm = {0}, validated and modified PUT = {1}", realm.getName(), validatedJsonString);
} }
@Override @Override
public void setupClientPoliciesOnExportingRealm(RealmModel realm, RealmRepresentation rep) { public ClientPoliciesRepresentation getClientPolicies(RealmModel realm) throws ClientPolicyException {
// client profiles that filter out builtin profiles..
ClientProfilesRepresentation filteredOutProfiles = null;
try { try {
filteredOutProfiles = getClientProfilesForExport(realm); ClientPoliciesRepresentation clientPolicies = ClientPoliciesUtil.getClientPoliciesRepresentation(session, realm);
} catch (ClientPolicyException e) { if (logger.isTraceEnabled()) {
// set as null logger.tracev("GET POLICIES :: realm = {0}, GET = {1}", realm.getName(), JsonSerialization.writeValueAsString(clientPolicies));
} }
return clientPolicies;
} catch (ClientPolicyException e) {
logger.warnv("GET CLIENT POLICIES FAILED :: error = {0}, error detail = {1}", e.getError(), e.getErrorDetail());
throw e;
} catch (IOException ioe) {
throw new RuntimeException("Unexpected exception when converting JSON to String", ioe);
}
}
@Override
public void updateRealmRepresentationFromModel(RealmModel realm, RealmRepresentation rep) {
try {
// client profiles that filter out global profiles..
ClientProfilesRepresentation filteredOutProfiles = getClientProfiles(realm, false);
rep.setClientProfiles(filteredOutProfiles); rep.setClientProfiles(filteredOutProfiles);
// client policies that filter out builtin and policies. ClientPoliciesRepresentation filteredOutPolicies = getClientPolicies(realm);
ClientPoliciesRepresentation filteredOutPolicies = null;
try {
filteredOutPolicies = getClientPoliciesForExport(realm);
} catch (ClientPolicyException e) {
// set as null
}
rep.setClientPolicies(filteredOutPolicies); rep.setClientPolicies(filteredOutPolicies);
} catch (ClientPolicyException cpe) {
throw new IllegalStateException("Exception during export client profiles or client policies", cpe);
}
} }
@Override @Override
public String getClientPolicies(RealmModel realm) { public void close() {
String json = getClientPoliciesJsonString(realm);
logger.tracev("GET POLICIES :: realm = {0}, GET = {1}", realm.getName(), json);
return json;
}
@Override
public String getClientProfilesOnKeycloakApp() {
return builtinClientProfilesJson;
}
@Override
public String getClientPoliciesOnKeycloakApp() {
return builtinClientPoliciesJson;
}
@Override
public String getClientProfilesJsonString(RealmModel realm) {
return realm.getAttribute(CLIENT_PROFILES);
}
@Override
public String getClientPoliciesJsonString(RealmModel realm) {
return realm.getAttribute(CLIENT_POLICIES);
}
private void setClientProfilesJsonString(RealmModel realm, String json) {
realm.setAttribute(CLIENT_PROFILES, json);
}
private void setClientPoliciesJsonString(RealmModel realm, String json) {
realm.setAttribute(CLIENT_POLICIES, json);
}
private String getValidatedClientProfilesJson(RealmModel realm, String profilesJson) throws ClientPolicyException {
ClientProfilesRepresentation validatedProfilesRep = ClientPoliciesUtil.getValidatedClientProfilesRepresentation(session, realm, profilesJson);
return ClientPoliciesUtil.convertClientProfilesRepresentationToJson(validatedProfilesRep);
}
private String getValidatedClientPoliciesJson(RealmModel realm, String policiesJson) throws ClientPolicyException {
ClientPoliciesRepresentation validatedPoliciesRep = ClientPoliciesUtil.getValidatedClientPoliciesRepresentation(session, realm, policiesJson);
return ClientPoliciesUtil.convertClientPoliciesRepresentationToJson(validatedPoliciesRep);
}
/**
* not return null
*/
private ClientProfilesRepresentation getClientProfilesForExport(RealmModel realm) throws ClientPolicyException {
ClientProfilesRepresentation profilesRep = ClientPoliciesUtil.getClientProfilesRepresentation(session, realm);
if (profilesRep == null || profilesRep.getProfiles() == null) {
return new ClientProfilesRepresentation();
}
// not export builtin profiles
List<ClientProfileRepresentation> filteredProfileRepList = profilesRep.getProfiles().stream().filter(profileRep->!profileRep.isBuiltin()).collect(Collectors.toList());
profilesRep.setProfiles(filteredProfileRepList);
return profilesRep;
}
/**
* not return null
*/
private ClientPoliciesRepresentation getClientPoliciesForExport(RealmModel realm) throws ClientPolicyException {
ClientPoliciesRepresentation policiesRep = ClientPoliciesUtil.getClientPoliciesRepresentation(session, realm);
if (policiesRep == null || policiesRep.getPolicies() == null) {
return new ClientPoliciesRepresentation();
}
policiesRep.getPolicies().stream().forEach(policyRep->{
if (policyRep.isBuiltin()) {
// only keeps name, builtin and enabled fields.
policyRep.setDescription(null);
policyRep.setConditions(null);
policyRep.setProfiles(null);
}
});
return policiesRep;
} }
} }

View file

@ -0,0 +1,87 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.services.clientpolicy;
import java.util.List;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.representations.idm.ClientProfileRepresentation;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class DefaultClientPolicyManagerFactory implements ClientPolicyManagerFactory {
private static final Logger logger = Logger.getLogger(DefaultClientPolicyManagerFactory.class);
// Global (builtin) profiles are loaded on booting keycloak at once.
// therefore, their representations are kept and remain unchanged.
// these are shared among all realms.
private volatile List<ClientProfileRepresentation> globalClientProfiles;
@Override
public ClientPolicyManager create(KeycloakSession session) {
return new DefaultClientPolicyManager(session, () -> getGlobalClientProfiles(session));
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return "default";
}
/**
* When this method is called, assumption is that CLIENT_POLICIES feature is enabled
*/
protected List<ClientProfileRepresentation> getGlobalClientProfiles(KeycloakSession session) {
if (globalClientProfiles == null) {
synchronized (this) {
if (globalClientProfiles == null) {
logger.trace("LOAD GLOBAL CLIENT PROFILES ON KEYCLOAK");
// load builtin profiles from keycloak-services
try {
this.globalClientProfiles = ClientPoliciesUtil.getValidatedGlobalClientProfilesRepresentation(session, getClass().getResourceAsStream("/keycloak-default-client-profiles.json"));
} catch (ClientPolicyException cpe) {
logger.warnv("LOAD GLOBAL PROFILES ON KEYCLOAK FAILED :: error = {0}, error detail = {1}", cpe.getError(), cpe.getErrorDetail());
throw new IllegalStateException(cpe);
}
}
}
}
return globalClientProfiles;
}
}

View file

@ -17,54 +17,24 @@
package org.keycloak.services.clientpolicy.condition; package org.keycloak.services.clientpolicy.condition;
import java.util.Optional;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyVote; import org.keycloak.services.clientpolicy.ClientPolicyVote;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public class AnyClientCondition implements ClientPolicyConditionProvider<AnyClientCondition.Configuration> { public class AnyClientCondition extends AbstractClientPolicyConditionProvider<ClientPolicyConditionConfigurationRepresentation> {
// to avoid null configuration, use vacant new instance to indicate that there is no configuration set up.
private Configuration configuration = new Configuration();
public AnyClientCondition(KeycloakSession session) { public AnyClientCondition(KeycloakSession session) {
super(session);
} }
@Override @Override
public void setupConfiguration(Configuration config) { public Class<ClientPolicyConditionConfigurationRepresentation> getConditionConfigurationClass() {
this.configuration = config; return ClientPolicyConditionConfigurationRepresentation.class;
}
@Override
public Class<Configuration> getConditionConfigurationClass() {
return Configuration.class;
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Configuration extends ClientPolicyConditionConfiguration {
@JsonProperty("is-negative-logic")
protected Boolean negativeLogic;
public Boolean isNegativeLogic() {
return negativeLogic;
}
public void setNegativeLogic(Boolean negativeLogic) {
this.negativeLogic = negativeLogic;
}
}
@Override
public boolean isNegativeLogic() {
return Optional.ofNullable(this.configuration.isNegativeLogic()).orElse(Boolean.FALSE).booleanValue();
} }
@Override @Override

View file

@ -24,31 +24,20 @@ import java.util.Optional;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyVote; import org.keycloak.services.clientpolicy.ClientPolicyVote;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public class ClientAccessTypeCondition implements ClientPolicyConditionProvider<ClientAccessTypeCondition.Configuration> { public class ClientAccessTypeCondition extends AbstractClientPolicyConditionProvider<ClientAccessTypeCondition.Configuration> {
private static final Logger logger = Logger.getLogger(ClientAccessTypeCondition.class); private static final Logger logger = Logger.getLogger(ClientAccessTypeCondition.class);
// to avoid null configuration, use vacant new instance to indicate that there is no configuration set up.
private Configuration configuration = new Configuration();
private final KeycloakSession session;
public ClientAccessTypeCondition(KeycloakSession session) { public ClientAccessTypeCondition(KeycloakSession session) {
this.session = session; super(session);
}
@Override
public void setupConfiguration(Configuration config) {
this.configuration = config;
} }
@Override @Override
@ -56,18 +45,7 @@ public class ClientAccessTypeCondition implements ClientPolicyConditionProvider<
return Configuration.class; return Configuration.class;
} }
@JsonIgnoreProperties(ignoreUnknown = true) public static class Configuration extends ClientPolicyConditionConfigurationRepresentation {
public static class Configuration extends ClientPolicyConditionConfiguration {
@JsonProperty("is-negative-logic")
protected Boolean negativeLogic;
public Boolean isNegativeLogic() {
return negativeLogic;
}
public void setNegativeLogic(Boolean negativeLogic) {
this.negativeLogic = negativeLogic;
}
protected List<String> type; protected List<String> type;
@ -80,11 +58,6 @@ public class ClientAccessTypeCondition implements ClientPolicyConditionProvider<
} }
} }
@Override
public boolean isNegativeLogic() {
return Optional.ofNullable(this.configuration.isNegativeLogic()).orElse(Boolean.FALSE).booleanValue();
}
@Override @Override
public String getProviderId() { public String getProviderId() {
return ClientAccessTypeConditionFactory.PROVIDER_ID; return ClientAccessTypeConditionFactory.PROVIDER_ID;

View file

@ -73,7 +73,7 @@ public class ClientAccessTypeConditionFactory implements ClientPolicyConditionPr
@Override @Override
public String getHelpText() { public String getHelpText() {
return "It uses the client's access type (confidential, public, bearer-only) to determine whether the policy is applied."; return "It uses the client's access type (confidential, public, bearer-only) to determine whether the policy is applied. Condition is checked during most of OpenID Connect requests (Authorization request, token requests, introspection endpoint request etc).";
} }
@Override @Override

View file

@ -19,7 +19,6 @@ package org.keycloak.services.clientpolicy.condition;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -27,31 +26,20 @@ import org.jboss.logging.Logger;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyVote; import org.keycloak.services.clientpolicy.ClientPolicyVote;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public class ClientRolesCondition implements ClientPolicyConditionProvider<ClientRolesCondition.Configuration> { public class ClientRolesCondition extends AbstractClientPolicyConditionProvider<ClientRolesCondition.Configuration> {
private static final Logger logger = Logger.getLogger(ClientRolesCondition.class); private static final Logger logger = Logger.getLogger(ClientRolesCondition.class);
// to avoid null configuration, use vacant new instance to indicate that there is no configuration set up.
private Configuration configuration = new Configuration();
private final KeycloakSession session;
public ClientRolesCondition(KeycloakSession session) { public ClientRolesCondition(KeycloakSession session) {
this.session = session; super(session);
}
@Override
public void setupConfiguration(Configuration config) {
this.configuration = config;
} }
@Override @Override
@ -59,18 +47,7 @@ public class ClientRolesCondition implements ClientPolicyConditionProvider<Clien
return Configuration.class; return Configuration.class;
} }
@JsonIgnoreProperties(ignoreUnknown = true) public static class Configuration extends ClientPolicyConditionConfigurationRepresentation {
public static class Configuration extends ClientPolicyConditionConfiguration {
@JsonProperty("is-negative-logic")
protected Boolean negativeLogic;
public Boolean isNegativeLogic() {
return negativeLogic;
}
public void setNegativeLogic(Boolean negativeLogic) {
this.negativeLogic = negativeLogic;
}
protected List<String> roles; protected List<String> roles;
@ -83,11 +60,6 @@ public class ClientRolesCondition implements ClientPolicyConditionProvider<Clien
} }
} }
@Override
public boolean isNegativeLogic() {
return Optional.ofNullable(this.configuration.isNegativeLogic()).orElse(Boolean.FALSE).booleanValue();
}
@Override @Override
public String getProviderId() { public String getProviderId() {
return ClientRolesConditionFactory.PROVIDER_ID; return ClientRolesConditionFactory.PROVIDER_ID;

View file

@ -66,7 +66,7 @@ public class ClientRolesConditionFactory implements ClientPolicyConditionProvide
@Override @Override
public String getHelpText() { public String getHelpText() {
return "The condition checks whether one of the specified client roles is applied to the client to determine whether the policy is applied."; return "The condition checks whether one of the specified client roles exists on the client to determine whether the policy is applied. This effectively allows client administrator to create client role of specified name on the client to make sure that particular client policy will be applied on requests of this client. Condition is checked during most of OpenID Connect requests (Authorization request, token requests, introspection endpoint request etc).";
} }
@Override @Override

View file

@ -21,7 +21,6 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -30,33 +29,22 @@ import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest; import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest;
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyVote; import org.keycloak.services.clientpolicy.ClientPolicyVote;
import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext; import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext;
import org.keycloak.services.clientpolicy.context.TokenRequestContext; import org.keycloak.services.clientpolicy.context.TokenRequestContext;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public class ClientScopesCondition implements ClientPolicyConditionProvider<ClientScopesCondition.Configuration> { public class ClientScopesCondition extends AbstractClientPolicyConditionProvider<ClientScopesCondition.Configuration> {
private static final Logger logger = Logger.getLogger(ClientScopesCondition.class); private static final Logger logger = Logger.getLogger(ClientScopesCondition.class);
// to avoid null configuration, use vacant new instance to indicate that there is no configuration set up.
private Configuration configuration = new Configuration();
private final KeycloakSession session;
public ClientScopesCondition(KeycloakSession session) { public ClientScopesCondition(KeycloakSession session) {
this.session = session; super(session);
}
@Override
public void setupConfiguration(Configuration config) {
this.configuration = config;
} }
@Override @Override
@ -64,18 +52,7 @@ public class ClientScopesCondition implements ClientPolicyConditionProvider<Clie
return Configuration.class; return Configuration.class;
} }
@JsonIgnoreProperties(ignoreUnknown = true) public static class Configuration extends ClientPolicyConditionConfigurationRepresentation {
public static class Configuration extends ClientPolicyConditionConfiguration {
@JsonProperty("is-negative-logic")
protected Boolean negativeLogic;
public Boolean isNegativeLogic() {
return negativeLogic;
}
public void setNegativeLogic(Boolean negativeLogic) {
this.negativeLogic = negativeLogic;
}
protected String type; protected String type;
protected List<String> scope; protected List<String> scope;
@ -97,11 +74,6 @@ public class ClientScopesCondition implements ClientPolicyConditionProvider<Clie
} }
} }
@Override
public boolean isNegativeLogic() {
return Optional.ofNullable(this.configuration.isNegativeLogic()).orElse(Boolean.FALSE).booleanValue();
}
@Override @Override
public String getProviderId() { public String getProviderId() {
return ClientScopesConditionFactory.PROVIDER_ID; return ClientScopesConditionFactory.PROVIDER_ID;

View file

@ -18,9 +18,11 @@
package org.keycloak.services.clientpolicy.condition; package org.keycloak.services.clientpolicy.condition;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import org.keycloak.Config.Scope; import org.keycloak.Config.Scope;
import org.keycloak.OAuth2Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigProperty;
@ -40,10 +42,13 @@ public class ClientScopesConditionFactory implements ClientPolicyConditionProvid
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static { static {
ProviderConfigProperty property; ProviderConfigProperty property = new ProviderConfigProperty(SCOPES, PROVIDER_ID + ".label", PROVIDER_ID + ".tooltip", ProviderConfigProperty.MULTIVALUED_STRING_TYPE, OAuth2Constants.OFFLINE_ACCESS);
property = new ProviderConfigProperty(SCOPES, PROVIDER_ID + ".label", PROVIDER_ID + ".tooltip", ProviderConfigProperty.MULTIVALUED_STRING_TYPE, "offline_access");
configProperties.add(property); configProperties.add(property);
property = new ProviderConfigProperty(TYPE, "Scope Type", "Default or Optional", ProviderConfigProperty.LIST_TYPE, OPTIONAL); property = new ProviderConfigProperty(TYPE, "Scope Type",
"If set to 'Default', condition evaluates to true if client has some default scopes of the values specified by the 'Expected Scopes' property. " +
"If set to 'Optional', condition evaluates to true if client has some optional scopes of the values specified by the 'Expected Scopes' property and at the same time, the scope were used as a value of 'scope' parameter in the request",
ProviderConfigProperty.LIST_TYPE, OPTIONAL);
property.setOptions(Arrays.asList(DEFAULT, OPTIONAL));
configProperties.add(property); configProperties.add(property);
} }
@ -71,7 +76,7 @@ public class ClientScopesConditionFactory implements ClientPolicyConditionProvid
@Override @Override
public String getHelpText() { public String getHelpText() {
return "It uses the scopes requested or assigned in advance to the client to determine whether the policy is applied to this client."; return "It uses the scopes requested or assigned in advance to the client to determine whether the policy is applied to this client. Condition is evaluated during OpenID Connect authorization request and/or token request.";
} }
@Override @Override

View file

@ -19,11 +19,11 @@ package org.keycloak.services.clientpolicy.condition;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.JsonWebToken; import org.keycloak.representations.JsonWebToken;
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyVote; import org.keycloak.services.clientpolicy.ClientPolicyVote;
@ -31,27 +31,17 @@ import org.keycloak.services.clientpolicy.context.ClientCRUDContext;
import org.keycloak.services.clientregistration.ClientRegistrationTokenUtils; import org.keycloak.services.clientregistration.ClientRegistrationTokenUtils;
import org.keycloak.util.TokenUtil; import org.keycloak.util.TokenUtil;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public class ClientUpdateContextCondition implements ClientPolicyConditionProvider<ClientUpdateContextCondition.Configuration> { public class ClientUpdateContextCondition extends AbstractClientPolicyConditionProvider<ClientUpdateContextCondition.Configuration> {
private static final Logger logger = Logger.getLogger(ClientUpdateContextCondition.class); private static final Logger logger = Logger.getLogger(ClientUpdateContextCondition.class);
// to avoid null configuration, use vacant new instance to indicate that there is no configuration set up.
private Configuration configuration = new Configuration();
private final KeycloakSession session;
public ClientUpdateContextCondition(KeycloakSession session) { public ClientUpdateContextCondition(KeycloakSession session) {
this.session = session; super(session);
}
@Override
public void setupConfiguration(Configuration config) {
this.configuration = config;
} }
@Override @Override
@ -59,18 +49,7 @@ public class ClientUpdateContextCondition implements ClientPolicyConditionProvid
return Configuration.class; return Configuration.class;
} }
@JsonIgnoreProperties(ignoreUnknown = true) public static class Configuration extends ClientPolicyConditionConfigurationRepresentation {
public static class Configuration extends ClientPolicyConditionConfiguration {
@JsonProperty("is-negative-logic")
protected Boolean negativeLogic;
public Boolean isNegativeLogic() {
return negativeLogic;
}
public void setNegativeLogic(Boolean negativeLogic) {
this.negativeLogic = negativeLogic;
}
@JsonProperty("update-client-source") @JsonProperty("update-client-source")
protected List<String> updateClientSource; protected List<String> updateClientSource;
@ -84,11 +63,6 @@ public class ClientUpdateContextCondition implements ClientPolicyConditionProvid
} }
} }
@Override
public boolean isNegativeLogic() {
return Optional.ofNullable(this.configuration.isNegativeLogic()).orElse(Boolean.FALSE).booleanValue();
}
@Override @Override
public String getProviderId() { public String getProviderId() {
return ClientUpdateContextConditionFactory.PROVIDER_ID; return ClientUpdateContextConditionFactory.PROVIDER_ID;

View file

@ -44,7 +44,11 @@ public class ClientUpdateContextConditionFactory implements ClientPolicyConditio
static { static {
ProviderConfigProperty property; ProviderConfigProperty property;
property = new ProviderConfigProperty(UPDATE_CLIENT_SOURCE, null, null, ProviderConfigProperty.MULTIVALUED_LIST_TYPE, BY_AUTHENTICATED_USER); property = new ProviderConfigProperty(UPDATE_CLIENT_SOURCE, "Update Client Context", "Specifies the context how is client created or updated. " +
"ByInitialAccessToken is usually OpenID Connect client registration with the initial access token. " +
"ByRegistrationAccessToken is usually OpenID Connect client update request with the registration access token. " +
"ByAuthenticatedUser is usually Admin REST request with the token on behalf of authenticated user or client (service account). ByAnonymous is usually anonymous OpenID Client registration request.",
ProviderConfigProperty.MULTIVALUED_LIST_TYPE, BY_AUTHENTICATED_USER);
List<String> updateProfileValues = Arrays.asList(BY_AUTHENTICATED_USER, BY_ANONYMOUS, BY_INITIAL_ACCESS_TOKEN, BY_REGISTRATION_ACCESS_TOKEN); List<String> updateProfileValues = Arrays.asList(BY_AUTHENTICATED_USER, BY_ANONYMOUS, BY_INITIAL_ACCESS_TOKEN, BY_REGISTRATION_ACCESS_TOKEN);
property.setOptions(updateProfileValues); property.setOptions(updateProfileValues);
configProperties.add(property); configProperties.add(property);

View file

@ -19,7 +19,6 @@ package org.keycloak.services.clientpolicy.condition;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -29,6 +28,7 @@ import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.representations.JsonWebToken; import org.keycloak.representations.JsonWebToken;
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyVote; import org.keycloak.services.clientpolicy.ClientPolicyVote;
@ -38,27 +38,15 @@ import org.keycloak.services.clientpolicy.context.ClientCRUDContext;
import org.keycloak.services.clientpolicy.context.DynamicClientRegisterContext; import org.keycloak.services.clientpolicy.context.DynamicClientRegisterContext;
import org.keycloak.services.clientpolicy.context.DynamicClientUpdateContext; import org.keycloak.services.clientpolicy.context.DynamicClientUpdateContext;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public class ClientUpdateSourceGroupsCondition implements ClientPolicyConditionProvider<ClientUpdateSourceGroupsCondition.Configuration> { public class ClientUpdateSourceGroupsCondition extends AbstractClientPolicyConditionProvider<ClientUpdateSourceGroupsCondition.Configuration> {
private static final Logger logger = Logger.getLogger(ClientUpdateSourceGroupsCondition.class); private static final Logger logger = Logger.getLogger(ClientUpdateSourceGroupsCondition.class);
// to avoid null configuration, use vacant new instance to indicate that there is no configuration set up.
private Configuration configuration = new Configuration();
private final KeycloakSession session;
public ClientUpdateSourceGroupsCondition(KeycloakSession session) { public ClientUpdateSourceGroupsCondition(KeycloakSession session) {
this.session = session; super(session);
}
@Override
public void setupConfiguration(Configuration config) {
this.configuration = config;
} }
@Override @Override
@ -66,18 +54,7 @@ public class ClientUpdateSourceGroupsCondition implements ClientPolicyConditionP
return Configuration.class; return Configuration.class;
} }
@JsonIgnoreProperties(ignoreUnknown = true) public static class Configuration extends ClientPolicyConditionConfigurationRepresentation {
public static class Configuration extends ClientPolicyConditionConfiguration {
@JsonProperty("is-negative-logic")
protected Boolean negativeLogic;
public Boolean isNegativeLogic() {
return negativeLogic;
}
public void setNegativeLogic(Boolean negativeLogic) {
this.negativeLogic = negativeLogic;
}
protected List<String> groups; protected List<String> groups;
@ -90,11 +67,6 @@ public class ClientUpdateSourceGroupsCondition implements ClientPolicyConditionP
} }
} }
@Override
public boolean isNegativeLogic() {
return Optional.ofNullable(this.configuration.isNegativeLogic()).orElse(Boolean.FALSE).booleanValue();
}
@Override @Override
public String getProviderId() { public String getProviderId() {
return ClientUpdateSourceGroupsConditionFactory.PROVIDER_ID; return ClientUpdateSourceGroupsConditionFactory.PROVIDER_ID;

View file

@ -21,36 +21,26 @@ import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyVote; import org.keycloak.services.clientpolicy.ClientPolicyVote;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public class ClientUpdateSourceHostsCondition implements ClientPolicyConditionProvider<ClientUpdateSourceHostsCondition.Configuration> { public class ClientUpdateSourceHostsCondition extends AbstractClientPolicyConditionProvider<ClientUpdateSourceHostsCondition.Configuration> {
private static final Logger logger = Logger.getLogger(ClientUpdateSourceHostsCondition.class); private static final Logger logger = Logger.getLogger(ClientUpdateSourceHostsCondition.class);
// to avoid null configuration, use vacant new instance to indicate that there is no configuration set up.
private Configuration configuration = new Configuration();
private final KeycloakSession session;
public ClientUpdateSourceHostsCondition(KeycloakSession session) { public ClientUpdateSourceHostsCondition(KeycloakSession session) {
this.session = session; super(session);
}
@Override
public void setupConfiguration(Configuration config) {
this.configuration = config;
} }
@Override @Override
@ -59,18 +49,7 @@ public class ClientUpdateSourceHostsCondition implements ClientPolicyConditionPr
} }
@JsonIgnoreProperties(ignoreUnknown = true) public static class Configuration extends ClientPolicyConditionConfigurationRepresentation {
public static class Configuration extends ClientPolicyConditionConfiguration {
@JsonProperty("is-negative-logic")
protected Boolean negativeLogic;
public Boolean isNegativeLogic() {
return negativeLogic;
}
public void setNegativeLogic(Boolean negativeLogic) {
this.negativeLogic = negativeLogic;
}
@JsonProperty("trusted-hosts") @JsonProperty("trusted-hosts")
protected List<String> trustedHosts; protected List<String> trustedHosts;
@ -84,11 +63,6 @@ public class ClientUpdateSourceHostsCondition implements ClientPolicyConditionPr
} }
} }
@Override
public boolean isNegativeLogic() {
return Optional.ofNullable(this.configuration.isNegativeLogic()).orElse(Boolean.FALSE).booleanValue();
}
@Override @Override
public String getProviderId() { public String getProviderId() {
return ClientUpdateSourceHostsConditionFactory.PROVIDER_ID; return ClientUpdateSourceHostsConditionFactory.PROVIDER_ID;

View file

@ -19,7 +19,6 @@ package org.keycloak.services.clientpolicy.condition;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -29,7 +28,9 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.JsonWebToken; import org.keycloak.representations.JsonWebToken;
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyVote; import org.keycloak.services.clientpolicy.ClientPolicyVote;
@ -39,27 +40,16 @@ import org.keycloak.services.clientpolicy.context.ClientCRUDContext;
import org.keycloak.services.clientpolicy.context.DynamicClientRegisterContext; import org.keycloak.services.clientpolicy.context.DynamicClientRegisterContext;
import org.keycloak.services.clientpolicy.context.DynamicClientUpdateContext; import org.keycloak.services.clientpolicy.context.DynamicClientUpdateContext;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public class ClientUpdateSourceRolesCondition implements ClientPolicyConditionProvider<ClientUpdateSourceRolesCondition.Configuration> { public class ClientUpdateSourceRolesCondition extends AbstractClientPolicyConditionProvider<ClientUpdateSourceRolesCondition.Configuration> {
private static final Logger logger = Logger.getLogger(ClientUpdateSourceRolesCondition.class); private static final Logger logger = Logger.getLogger(ClientUpdateSourceRolesCondition.class);
// to avoid null configuration, use vacant new instance to indicate that there is no configuration set up.
private Configuration configuration = new Configuration();
private final KeycloakSession session;
public ClientUpdateSourceRolesCondition(KeycloakSession session) { public ClientUpdateSourceRolesCondition(KeycloakSession session) {
this.session = session; super(session);
}
@Override
public void setupConfiguration(Configuration config) {
this.configuration = config;
} }
@Override @Override
@ -67,18 +57,7 @@ public class ClientUpdateSourceRolesCondition implements ClientPolicyConditionPr
return Configuration.class; return Configuration.class;
} }
@JsonIgnoreProperties(ignoreUnknown = true) public static class Configuration extends ClientPolicyConditionConfigurationRepresentation {
public static class Configuration extends ClientPolicyConditionConfiguration {
@JsonProperty("is-negative-logic")
protected Boolean negativeLogic;
public Boolean isNegativeLogic() {
return negativeLogic;
}
public void setNegativeLogic(Boolean negativeLogic) {
this.negativeLogic = negativeLogic;
}
protected List<String> roles; protected List<String> roles;
@ -91,11 +70,6 @@ public class ClientUpdateSourceRolesCondition implements ClientPolicyConditionPr
} }
} }
@Override
public boolean isNegativeLogic() {
return Optional.ofNullable(this.configuration.isNegativeLogic()).orElse(Boolean.FALSE).booleanValue();
}
@Override @Override
public String getProviderId() { public String getProviderId() {
return ClientUpdateSourceRolesConditionFactory.PROVIDER_ID; return ClientUpdateSourceRolesConditionFactory.PROVIDER_ID;
@ -147,28 +121,21 @@ public class ClientUpdateSourceRolesCondition implements ClientPolicyConditionPr
Set<String> expectedRoles = instantiateRolesForMatching(); Set<String> expectedRoles = instantiateRolesForMatching();
if (expectedRoles == null) return false; if (expectedRoles == null) return false;
if (logger.isTraceEnabled()) {
// user.getRoleMappingsStream() never returns null according to {@link UserModel.getRoleMappingsStream} // user.getRoleMappingsStream() never returns null according to {@link UserModel.getRoleMappingsStream}
Set<String> roles = user.getRoleMappingsStream().map(RoleModel::getName).collect(Collectors.toSet()); Set<String> roles = user.getRoleMappingsStream().map(RoleModel::getName).collect(Collectors.toSet());
if (logger.isTraceEnabled()) {
roles.forEach(i -> logger.tracev("user role = {0}", i)); roles.forEach(i -> logger.tracev("user role = {0}", i));
expectedRoles.forEach(i -> logger.tracev("roles expected = {0}", i)); expectedRoles.forEach(i -> logger.tracev("roles expected = {0}", i));
} }
RealmModel realm = session.getContext().getRealm(); RealmModel realm = session.getContext().getRealm();
boolean isMatched = expectedRoles.stream().anyMatch(i->{ for (String roleName : expectedRoles) {
if (realm.getRole(i) != null && user.hasRole(realm.getRole(i))) { RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
return true; if (role == null) continue;
} if (user.hasRole(role)) return true;
return realm.getClientsStream().anyMatch(j->{
if (j.getRole(i) != null && user.hasRole(j.getRole(i))) {
return true;
} }
return false; return false;
});
});
return isMatched;
} }
private Set<String> instantiateRolesForMatching() { private Set<String> instantiateRolesForMatching() {

View file

@ -20,15 +20,14 @@ package org.keycloak.services.clientpolicy.executor;
import org.keycloak.OAuthErrorException; import org.keycloak.OAuthErrorException;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public class ConfidentialClientAcceptExecutor implements ClientPolicyExecutorProvider<ClientPolicyExecutorConfiguration> { public class ConfidentialClientAcceptExecutor implements ClientPolicyExecutorProvider<ClientPolicyExecutorConfigurationRepresentation> {
protected final KeycloakSession session; protected final KeycloakSession session;

View file

@ -19,6 +19,7 @@ package org.keycloak.services.clientpolicy.executor;
import org.keycloak.events.Errors; import org.keycloak.events.Errors;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
@ -27,7 +28,7 @@ import org.keycloak.services.clientpolicy.context.ClientCRUDContext;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public class ConsentRequiredExecutor implements ClientPolicyExecutorProvider<ClientPolicyExecutorConfiguration> { public class ConsentRequiredExecutor implements ClientPolicyExecutorProvider<ClientPolicyExecutorConfigurationRepresentation> {
@Override @Override
public void executeOnEvent(ClientPolicyContext context) throws ClientPolicyException { public void executeOnEvent(ClientPolicyContext context) throws ClientPolicyException {

View file

@ -26,6 +26,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.representations.RefreshToken; import org.keycloak.representations.RefreshToken;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
@ -36,7 +37,6 @@ import org.keycloak.services.clientpolicy.context.TokenRevokeContext;
import org.keycloak.services.clientpolicy.context.UserInfoRequestContext; import org.keycloak.services.clientpolicy.context.UserInfoRequestContext;
import org.keycloak.services.util.MtlsHoKTokenUtil; import org.keycloak.services.util.MtlsHoKTokenUtil;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
@ -61,8 +61,7 @@ public class HolderOfKeyEnforceExecutor implements ClientPolicyExecutorProvider<
return Configuration.class; return Configuration.class;
} }
@JsonIgnoreProperties(ignoreUnknown = true) public static class Configuration extends ClientPolicyExecutorConfigurationRepresentation {
public static class Configuration extends ClientPolicyExecutorConfiguration {
@JsonProperty("is-augment") @JsonProperty("is-augment")
protected Boolean augment; protected Boolean augment;

View file

@ -33,7 +33,7 @@ public class HolderOfKeyEnforceExecutorFactory implements ClientPolicyExecutorPr
public static final String IS_AUGMENT = "is-augment"; public static final String IS_AUGMENT = "is-augment";
private static final ProviderConfigProperty IS_AUGMENT_PROPERTY = new ProviderConfigProperty( private static final ProviderConfigProperty IS_AUGMENT_PROPERTY = new ProviderConfigProperty(
IS_AUGMENT, null, null, ProviderConfigProperty.BOOLEAN_TYPE, false); IS_AUGMENT, "Augment Configuration", "If On, then the during client creation or update, the configuration of the client will be augmented to use MTLS HoK token", ProviderConfigProperty.BOOLEAN_TYPE, false);
@Override @Override
public ClientPolicyExecutorProvider create(KeycloakSession session) { public ClientPolicyExecutorProvider create(KeycloakSession session) {

View file

@ -35,6 +35,7 @@ import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest
import org.keycloak.protocol.oidc.utils.OAuth2Code; import org.keycloak.protocol.oidc.utils.OAuth2Code;
import org.keycloak.protocol.oidc.utils.OAuth2CodeParser; import org.keycloak.protocol.oidc.utils.OAuth2CodeParser;
import org.keycloak.protocol.oidc.utils.OIDCResponseType; import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
@ -42,7 +43,6 @@ import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext;
import org.keycloak.services.clientpolicy.context.ClientCRUDContext; import org.keycloak.services.clientpolicy.context.ClientCRUDContext;
import org.keycloak.services.clientpolicy.context.TokenRequestContext; import org.keycloak.services.clientpolicy.context.TokenRequestContext;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
@ -70,8 +70,7 @@ public class PKCEEnforceExecutor implements ClientPolicyExecutorProvider<PKCEEnf
return Configuration.class; return Configuration.class;
} }
@JsonIgnoreProperties(ignoreUnknown = true) public static class Configuration extends ClientPolicyExecutorConfigurationRepresentation {
public static class Configuration extends ClientPolicyExecutorConfiguration {
@JsonProperty("is-augment") @JsonProperty("is-augment")
protected Boolean augment; protected Boolean augment;

View file

@ -36,7 +36,7 @@ public class PKCEEnforceExecutorFactory implements ClientPolicyExecutorProviderF
public static final String IS_AUGMENT = "is-augment"; public static final String IS_AUGMENT = "is-augment";
private static final ProviderConfigProperty IS_AUGMENT_PROPERTY = new ProviderConfigProperty( private static final ProviderConfigProperty IS_AUGMENT_PROPERTY = new ProviderConfigProperty(
IS_AUGMENT, null, null, ProviderConfigProperty.BOOLEAN_TYPE, false); IS_AUGMENT, "Augment Configuration", "If On, then the during client creation or update, the configuration of the client will be augmented to enforce usage of PKCE", ProviderConfigProperty.BOOLEAN_TYPE, false);
@Override @Override
public ClientPolicyExecutorProvider create(KeycloakSession session) { public ClientPolicyExecutorProvider create(KeycloakSession session) {

View file

@ -21,12 +21,12 @@ import java.util.List;
import org.keycloak.OAuthErrorException; import org.keycloak.OAuthErrorException;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.context.ClientCRUDContext; import org.keycloak.services.clientpolicy.context.ClientCRUDContext;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
@ -51,8 +51,7 @@ public class SecureClientAuthEnforceExecutor implements ClientPolicyExecutorProv
return Configuration.class; return Configuration.class;
} }
@JsonIgnoreProperties(ignoreUnknown = true) public static class Configuration extends ClientPolicyExecutorConfigurationRepresentation {
public static class Configuration extends ClientPolicyExecutorConfiguration {
@JsonProperty("client-authns") @JsonProperty("client-authns")
protected List<String> clientAuthns; protected List<String> clientAuthns;
@JsonProperty("client-authns-augment") @JsonProperty("client-authns-augment")

View file

@ -20,12 +20,15 @@ package org.keycloak.services.clientpolicy.executor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import org.keycloak.Config.Scope; import org.keycloak.Config.Scope;
import org.keycloak.authentication.ClientAuthenticator;
import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator; import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderFactory;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
@ -38,12 +41,7 @@ public class SecureClientAuthEnforceExecutorFactory implements ClientPolicyExecu
public static final String CLIENT_AUTHNS = "client-authns"; public static final String CLIENT_AUTHNS = "client-authns";
public static final String CLIENT_AUTHNS_AUGMENT = "client-authns-augment"; public static final String CLIENT_AUTHNS_AUGMENT = "client-authns-augment";
private static final ProviderConfigProperty IS_AUGMENT_PROPERTY = new ProviderConfigProperty( private List<ProviderConfigProperty> configProperties = new ArrayList<>();
IS_AUGMENT, null, null, ProviderConfigProperty.BOOLEAN_TYPE, false);
private static final ProviderConfigProperty CLIENTAUTHNS_PROPERTY = new ProviderConfigProperty(
CLIENT_AUTHNS, null, null, ProviderConfigProperty.MULTIVALUED_STRING_TYPE, null);
private static final ProviderConfigProperty CLIENTAUTHNS_AUGMENT = new ProviderConfigProperty(
CLIENT_AUTHNS_AUGMENT, null, null, ProviderConfigProperty.STRING_TYPE, JWTClientAuthenticator.PROVIDER_ID);
@Override @Override
public ClientPolicyExecutorProvider create(KeycloakSession session) { public ClientPolicyExecutorProvider create(KeycloakSession session) {
@ -56,6 +54,25 @@ public class SecureClientAuthEnforceExecutorFactory implements ClientPolicyExecu
@Override @Override
public void postInit(KeycloakSessionFactory factory) { public void postInit(KeycloakSessionFactory factory) {
ProviderConfigProperty isAugmentProperty = new ProviderConfigProperty(
IS_AUGMENT, "Augment Configuration", "If On, then the during client creation or update, the configuration of the client will be augmented to enforce the authentication method to new clients",
ProviderConfigProperty.BOOLEAN_TYPE, false);
List<String> clientAuthProviders = factory.getProviderFactoriesStream(ClientAuthenticator.class)
.map(ProviderFactory::getId)
.collect(Collectors.toList());
ProviderConfigProperty clientAuthnsProperty = new ProviderConfigProperty(
CLIENT_AUTHNS, "Client Authentication Methods", "List of available client authentication methods, which are allowed for clients to use. Other client authentication methods will not be allowed.",
ProviderConfigProperty.MULTIVALUED_LIST_TYPE, null);
clientAuthnsProperty.setOptions(clientAuthProviders);
ProviderConfigProperty clientAuthnsAugment = new ProviderConfigProperty(
CLIENT_AUTHNS_AUGMENT, "Augment Client Authentication Method", "If 'Augment Configuration' is ON, then this client authentication method will be set as the authentication method to new clients",
ProviderConfigProperty.LIST_TYPE, JWTClientAuthenticator.PROVIDER_ID);
clientAuthnsAugment.setOptions(clientAuthProviders);
configProperties = Arrays.asList(isAugmentProperty, clientAuthnsProperty, clientAuthnsAugment);
} }
@Override @Override
@ -74,7 +91,7 @@ public class SecureClientAuthEnforceExecutorFactory implements ClientPolicyExecu
@Override @Override
public List<ProviderConfigProperty> getConfigProperties() { public List<ProviderConfigProperty> getConfigProperties() {
return new ArrayList<>(Arrays.asList(IS_AUGMENT_PROPERTY, CLIENTAUTHNS_PROPERTY, CLIENTAUTHNS_AUGMENT)); return configProperties;
} }
} }

View file

@ -28,6 +28,7 @@ import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.protocol.oidc.OIDCConfigAttributes; import org.keycloak.protocol.oidc.OIDCConfigAttributes;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.context.AdminClientRegisterContext; import org.keycloak.services.clientpolicy.context.AdminClientRegisterContext;
@ -40,7 +41,7 @@ import org.keycloak.services.clientpolicy.context.DynamicClientUpdateContext;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public class SecureClientRegisteringUriEnforceExecutor implements ClientPolicyExecutorProvider<ClientPolicyExecutorConfiguration> { public class SecureClientRegisteringUriEnforceExecutor implements ClientPolicyExecutorProvider<ClientPolicyExecutorConfigurationRepresentation> {
private static final Logger logger = Logger.getLogger(SecureClientRegisteringUriEnforceExecutor.class); private static final Logger logger = Logger.getLogger(SecureClientRegisteringUriEnforceExecutor.class);

View file

@ -31,13 +31,12 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest; import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest;
import org.keycloak.protocol.oidc.endpoints.request.AuthzEndpointRequestParser; import org.keycloak.protocol.oidc.endpoints.request.AuthzEndpointRequestParser;
import org.keycloak.protocol.oidc.utils.OIDCResponseType; import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.services.Urls; import org.keycloak.services.Urls;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext; import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext;
import org.keycloak.services.clientpolicy.executor.PKCEEnforceExecutor.Configuration;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
@ -80,8 +79,7 @@ public class SecureRequestObjectExecutor implements ClientPolicyExecutorProvider
return Configuration.class; return Configuration.class;
} }
@JsonIgnoreProperties(ignoreUnknown = true) public static class Configuration extends ClientPolicyExecutorConfigurationRepresentation {
public static class Configuration extends ClientPolicyExecutorConfiguration {
@JsonProperty("available-period") @JsonProperty("available-period")
protected Integer availablePeriod; protected Integer availablePeriod;
@JsonProperty("verify-nbf") @JsonProperty("verify-nbf")

View file

@ -37,7 +37,14 @@ public class SecureRequestObjectExecutorFactory implements ClientPolicyExecutorP
public static final String VERIFY_NBF = "verify-nbf"; public static final String VERIFY_NBF = "verify-nbf";
private static final ProviderConfigProperty VERIFY_NBF_PROPERTY = new ProviderConfigProperty( private static final ProviderConfigProperty VERIFY_NBF_PROPERTY = new ProviderConfigProperty(
VERIFY_NBF, null, null, ProviderConfigProperty.BOOLEAN_TYPE, true); VERIFY_NBF, "Verify Not-Before", "If ON, then it will be verified if 'request' object used in OIDC authorization request contains not-before " +
"claim and this claim will be validated", ProviderConfigProperty.BOOLEAN_TYPE, true);
public static final String AVAILABLE_PERIOD = "available-period";
private static final ProviderConfigProperty AVAILABLE_PERIOD_PROPERTY = new ProviderConfigProperty(
AVAILABLE_PERIOD, "Available Period", "The maximum period in seconds for which the 'request' object used in OIDC authorization request is considered valid. " +
"It is used if 'Verify Not-Before' is ON.", ProviderConfigProperty.STRING_TYPE, "3600");
@Override @Override
public ClientPolicyExecutorProvider create(KeycloakSession session) { public ClientPolicyExecutorProvider create(KeycloakSession session) {
@ -68,7 +75,7 @@ public class SecureRequestObjectExecutorFactory implements ClientPolicyExecutorP
@Override @Override
public List<ProviderConfigProperty> getConfigProperties() { public List<ProviderConfigProperty> getConfigProperties() {
return new ArrayList<>(Arrays.asList(VERIFY_NBF_PROPERTY)); return new ArrayList<>(Arrays.asList(VERIFY_NBF_PROPERTY, AVAILABLE_PERIOD_PROPERTY));
} }
} }

View file

@ -22,6 +22,7 @@ import org.keycloak.OAuthErrorException;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest; import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest;
import org.keycloak.protocol.oidc.utils.OIDCResponseType; import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext; import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext;
@ -29,7 +30,7 @@ import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public class SecureResponseTypeExecutor implements ClientPolicyExecutorProvider<ClientPolicyExecutorConfiguration> { public class SecureResponseTypeExecutor implements ClientPolicyExecutorProvider<ClientPolicyExecutorConfigurationRepresentation> {
private static final Logger logger = Logger.getLogger(SecureResponseTypeExecutor.class); private static final Logger logger = Logger.getLogger(SecureResponseTypeExecutor.class);

View file

@ -22,6 +22,7 @@ import org.keycloak.OAuthErrorException;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest; import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest;
import org.keycloak.protocol.oidc.utils.OIDCResponseType; import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext; import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext;
@ -30,7 +31,7 @@ import org.keycloak.util.TokenUtil;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public class SecureSessionEnforceExecutor implements ClientPolicyExecutorProvider<ClientPolicyExecutorConfiguration> { public class SecureSessionEnforceExecutor implements ClientPolicyExecutorProvider<ClientPolicyExecutorConfigurationRepresentation> {
private static final Logger logger = Logger.getLogger(SecureSessionEnforceExecutor.class); private static final Logger logger = Logger.getLogger(SecureSessionEnforceExecutor.class);

View file

@ -19,9 +19,11 @@ package org.keycloak.services.clientpolicy.executor;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -29,6 +31,7 @@ import org.keycloak.OAuthErrorException;
import org.keycloak.crypto.Algorithm; import org.keycloak.crypto.Algorithm;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.protocol.oidc.OIDCConfigAttributes; import org.keycloak.protocol.oidc.OIDCConfigAttributes;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
@ -37,7 +40,6 @@ import org.keycloak.services.clientpolicy.context.AdminClientUpdateContext;
import org.keycloak.services.clientpolicy.context.DynamicClientRegisterContext; import org.keycloak.services.clientpolicy.context.DynamicClientRegisterContext;
import org.keycloak.services.clientpolicy.context.DynamicClientUpdateContext; import org.keycloak.services.clientpolicy.context.DynamicClientUpdateContext;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
@ -61,6 +63,15 @@ public class SecureSigningAlgorithmEnforceExecutor implements ClientPolicyExecut
private static final String DEFAULT_ALGORITHM_VALUE = Algorithm.PS256; private static final String DEFAULT_ALGORITHM_VALUE = Algorithm.PS256;
static final Set<String> ALLOWED_ALGORITHMS = new LinkedHashSet<>(Arrays.asList(
Algorithm.PS256,
Algorithm.PS384,
Algorithm.PS512,
Algorithm.ES256,
Algorithm.ES384,
Algorithm.ES512
));
public SecureSigningAlgorithmEnforceExecutor(KeycloakSession session) { public SecureSigningAlgorithmEnforceExecutor(KeycloakSession session) {
this.session = session; this.session = session;
} }
@ -81,8 +92,7 @@ public class SecureSigningAlgorithmEnforceExecutor implements ClientPolicyExecut
return Configuration.class; return Configuration.class;
} }
@JsonIgnoreProperties(ignoreUnknown = true) public static class Configuration extends ClientPolicyExecutorConfigurationRepresentation {
public static class Configuration extends ClientPolicyExecutorConfiguration {
@JsonProperty("default-algorithm") @JsonProperty("default-algorithm")
protected String defaultAlgorithm; protected String defaultAlgorithm;
@ -165,16 +175,7 @@ public class SecureSigningAlgorithmEnforceExecutor implements ClientPolicyExecut
} }
private static boolean isSecureAlgorithm(String sigAlg) { private static boolean isSecureAlgorithm(String sigAlg) {
switch (sigAlg) { return ALLOWED_ALGORITHMS.contains(sigAlg);
case Algorithm.PS256:
case Algorithm.PS384:
case Algorithm.PS512:
case Algorithm.ES256:
case Algorithm.ES384:
case Algorithm.ES512:
return true;
}
return false;
} }
} }

View file

@ -19,6 +19,7 @@ package org.keycloak.services.clientpolicy.executor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.keycloak.Config.Scope; import org.keycloak.Config.Scope;
@ -37,7 +38,8 @@ public class SecureSigningAlgorithmEnforceExecutorFactory implements ClientPolic
public static final String DEFAULT_ALGORITHM = "default-algorithm"; public static final String DEFAULT_ALGORITHM = "default-algorithm";
private static final ProviderConfigProperty DEFAULT_ALGORITHM_PROPERTY = new ProviderConfigProperty( private static final ProviderConfigProperty DEFAULT_ALGORITHM_PROPERTY = new ProviderConfigProperty(
DEFAULT_ALGORITHM, null, null, ProviderConfigProperty.STRING_TYPE, Algorithm.PS256); DEFAULT_ALGORITHM, "Default Algorithm", "Default signature algorithm, which will be set to clients during client registration/update in case that client does not specify any algorithm",
ProviderConfigProperty.LIST_TYPE, Algorithm.PS256, new LinkedList<>(SecureSigningAlgorithmEnforceExecutor.ALLOWED_ALGORITHMS).toArray(new String[] {}));
@Override @Override
public ClientPolicyExecutorProvider create(KeycloakSession session) { public ClientPolicyExecutorProvider create(KeycloakSession session) {
@ -63,7 +65,7 @@ public class SecureSigningAlgorithmEnforceExecutorFactory implements ClientPolic
@Override @Override
public String getHelpText() { public String getHelpText() {
return "It refuses the client whose signature algorithms are considered not to be secure. It accepts ES256, ES384, ES512, PS256, PS384 and PS512."; return "It refuses the client whose signature algorithms are considered not to be secure. This is applied by server for signing ID Token, UserInfo and Access Token. Also it is used by client for Token Endpoint Authentication signature algorithm (for JWT client authenticators) and OIDC Request object. It accepts ES256, ES384, ES512, PS256, PS384 and PS512.";
} }
@Override @Override

View file

@ -23,16 +23,15 @@ import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException; import org.keycloak.OAuthErrorException;
import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
import org.keycloak.common.util.ObjectUtil; import org.keycloak.common.util.ObjectUtil;
import org.keycloak.crypto.Algorithm; import org.keycloak.crypto.Algorithm;
import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException; import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
public class SecureSigningAlgorithmForSignedJwtEnforceExecutor implements ClientPolicyExecutorProvider<SecureSigningAlgorithmForSignedJwtEnforceExecutor.Configuration> { public class SecureSigningAlgorithmForSignedJwtEnforceExecutor implements ClientPolicyExecutorProvider<SecureSigningAlgorithmForSignedJwtEnforceExecutor.Configuration> {
@ -61,8 +60,7 @@ public class SecureSigningAlgorithmForSignedJwtEnforceExecutor implements Client
return SecureSigningAlgorithmForSignedJwtEnforceExecutorFactory.PROVIDER_ID; return SecureSigningAlgorithmForSignedJwtEnforceExecutorFactory.PROVIDER_ID;
} }
@JsonIgnoreProperties(ignoreUnknown = true) public static class Configuration extends ClientPolicyExecutorConfigurationRepresentation {
public static class Configuration extends ClientPolicyExecutorConfiguration {
@JsonProperty("require-client-assertion") @JsonProperty("require-client-assertion")
protected Boolean requireClientAssertion; protected Boolean requireClientAssertion;

View file

@ -34,7 +34,7 @@ public class SecureSigningAlgorithmForSignedJwtEnforceExecutorFactory implements
public static final String REQUIRE_CLIENT_ASSERTION = "require-client-assertion"; public static final String REQUIRE_CLIENT_ASSERTION = "require-client-assertion";
private static final ProviderConfigProperty REQUIRE_CLIENT_ASSERTION_PROPERTY = new ProviderConfigProperty( private static final ProviderConfigProperty REQUIRE_CLIENT_ASSERTION_PROPERTY = new ProviderConfigProperty(
REQUIRE_CLIENT_ASSERTION, null, null, ProviderConfigProperty.BOOLEAN_TYPE, false); REQUIRE_CLIENT_ASSERTION, "Require Client Assertion", "If this is ON, then parameter 'client_assertion' will be required and request will fail if it is not present. If false, then parameter 'client_assertion' is not required. When 'client_assertion' parameter is present, then the algorithm on the JWT from specified client assertion is always checked regardless of the value of this switch", ProviderConfigProperty.BOOLEAN_TYPE, false);
@Override @Override
public ClientPolicyExecutorProvider create(KeycloakSession session) { public ClientPolicyExecutorProvider create(KeycloakSession session) {

View file

@ -124,7 +124,7 @@ public class RealmManager {
createDefaultClientScopes(realm); createDefaultClientScopes(realm);
setupAuthorizationServices(realm); setupAuthorizationServices(realm);
setupClientRegistrations(realm); setupClientRegistrations(realm);
setupClientPolicies(realm); session.clientPolicy().setupClientPoliciesOnCreatedRealm(realm);
fireRealmPostCreate(realm); fireRealmPostCreate(realm);
@ -599,7 +599,7 @@ public class RealmManager {
MigrationModelManager.migrateImport(session, realm, rep, skipUserDependent); MigrationModelManager.migrateImport(session, realm, rep, skipUserDependent);
} }
setupClientPolicies(realm, rep); session.clientPolicy().updateRealmModelFromRepresentation(realm, rep);
fireRealmPostCreate(realm); fireRealmPostCreate(realm);
@ -714,14 +714,6 @@ public class RealmManager {
DefaultClientRegistrationPolicies.addDefaultPolicies(realm); DefaultClientRegistrationPolicies.addDefaultPolicies(realm);
} }
private void setupClientPolicies(RealmModel realm, RealmRepresentation rep) {
session.clientPolicy().setupClientPoliciesOnImportedRealm(realm, rep);
}
private void setupClientPolicies(RealmModel realm) {
session.clientPolicy().setupClientPoliciesOnCreatedRealm(realm);
}
private void fireRealmPostCreate(RealmModel realm) { private void fireRealmPostCreate(RealmModel realm) {
session.getKeycloakSessionFactory().publish(new RealmModel.RealmPostCreateEvent() { session.getKeycloakSessionFactory().publish(new RealmModel.RealmPostCreateEvent() {
@Override @Override

View file

@ -172,8 +172,6 @@ public class KeycloakApplication extends Application {
} }
// TODO up here ^^ // TODO up here ^^
session.clientPolicy().setupClientPoliciesOnKeycloakApp("/keycloak-default-client-profiles.json", "/keycloak-default-client-policies.json");
ApplianceBootstrap applianceBootstrap = new ApplianceBootstrap(session); ApplianceBootstrap applianceBootstrap = new ApplianceBootstrap(session);
exportImportManager[0] = new ExportImportManager(session); exportImportManager[0] = new ExportImportManager(session);

View file

@ -17,6 +17,7 @@
package org.keycloak.services.resources.admin; package org.keycloak.services.resources.admin;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.PUT; import javax.ws.rs.PUT;
@ -32,6 +33,7 @@ import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse; import org.jboss.resteasy.spi.HttpResponse;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.ClientPoliciesRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
@ -58,19 +60,23 @@ public class ClientPoliciesResource {
@GET @GET
@NoCache @NoCache
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public String getPolicies() { public ClientPoliciesRepresentation getPolicies() {
auth.realm().requireViewRealm(); auth.realm().requireViewRealm();
try {
return session.clientPolicy().getClientPolicies(realm); return session.clientPolicy().getClientPolicies(realm);
} catch (ClientPolicyException e) {
throw new BadRequestException(Response.status(Status.BAD_REQUEST).entity(e.getError()).build());
}
} }
@PUT @PUT
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public Response updatePolicies(final String json) { public Response updatePolicies(final ClientPoliciesRepresentation clientPolicies) {
auth.realm().requireManageRealm(); auth.realm().requireManageRealm();
try { try {
session.clientPolicy().updateClientPolicies(realm, json); session.clientPolicy().updateClientPolicies(realm, clientPolicies);
} catch (ClientPolicyException e) { } catch (ClientPolicyException e) {
return Response.status(Status.BAD_REQUEST).entity(e.getError()).build(); return Response.status(Status.BAD_REQUEST).entity(e.getError()).build();
} }

View file

@ -17,10 +17,12 @@
package org.keycloak.services.resources.admin; package org.keycloak.services.resources.admin;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.PUT; import javax.ws.rs.PUT;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@ -32,6 +34,7 @@ import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse; import org.jboss.resteasy.spi.HttpResponse;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.ClientProfilesRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
@ -58,19 +61,23 @@ public class ClientProfilesResource {
@GET @GET
@NoCache @NoCache
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public String getProfiles() { public ClientProfilesRepresentation getProfiles(@QueryParam("include-global-profiles") boolean includeGlobalProfiles) {
auth.realm().requireViewRealm(); auth.realm().requireViewRealm();
return session.clientPolicy().getClientProfiles(realm); try {
return session.clientPolicy().getClientProfiles(realm, includeGlobalProfiles);
} catch (ClientPolicyException e) {
throw new BadRequestException(Response.status(Status.BAD_REQUEST).entity(e.getError()).build());
}
} }
@PUT @PUT
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public Response updateProfiles(final String json) { public Response updateProfiles(final ClientProfilesRepresentation clientProfiles) {
auth.realm().requireManageRealm(); auth.realm().requireManageRealm();
try { try {
session.clientPolicy().updateClientProfiles(realm, json); session.clientPolicy().updateClientProfiles(realm, clientProfiles);
} catch (ClientPolicyException e) { } catch (ClientPolicyException e) {
return Response.status(Status.BAD_REQUEST).entity(e.getError()).build(); return Response.status(Status.BAD_REQUEST).entity(e.getError()).build();
} }

View file

@ -57,6 +57,7 @@ import org.keycloak.KeyPairVerifier;
import org.keycloak.authentication.CredentialRegistrator; import org.keycloak.authentication.CredentialRegistrator;
import org.keycloak.authentication.RequiredActionProvider; import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.common.ClientConnection; import org.keycloak.common.ClientConnection;
import org.keycloak.common.Profile;
import org.keycloak.common.VerificationException; import org.keycloak.common.VerificationException;
import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.PemUtils;
import org.keycloak.email.EmailTemplateProvider; import org.keycloak.email.EmailTemplateProvider;
@ -113,6 +114,7 @@ import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluato
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement; import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
import org.keycloak.services.resources.admin.permissions.AdminPermissions; import org.keycloak.services.resources.admin.permissions.AdminPermissions;
import org.keycloak.representations.idm.LDAPCapabilityRepresentation; import org.keycloak.representations.idm.LDAPCapabilityRepresentation;
import org.keycloak.utils.ProfileHelper;
import org.keycloak.utils.ReservedCharValidator; import org.keycloak.utils.ReservedCharValidator;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
@ -371,7 +373,7 @@ public class RealmAdminResource {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public RealmRepresentation getRealm() { public RealmRepresentation getRealm() {
if (auth.realm().canViewRealm()) { if (auth.realm().canViewRealm()) {
return ModelToRepresentation.toRepresentation(realm, false); return ModelToRepresentation.toRepresentation(session, realm, false);
} else { } else {
auth.realm().requireViewRealmNameList(); auth.realm().requireViewRealmNameList();
@ -379,7 +381,7 @@ public class RealmAdminResource {
rep.setRealm(realm.getName()); rep.setRealm(realm.getName());
if (auth.realm().canViewIdentityProviders()) { if (auth.realm().canViewIdentityProviders()) {
RealmRepresentation r = ModelToRepresentation.toRepresentation(realm, false); RealmRepresentation r = ModelToRepresentation.toRepresentation(session, realm, false);
rep.setIdentityProviders(r.getIdentityProviders()); rep.setIdentityProviders(r.getIdentityProviders());
rep.setIdentityProviderMappers(r.getIdentityProviderMappers()); rep.setIdentityProviderMappers(r.getIdentityProviderMappers());
} }
@ -1209,6 +1211,7 @@ public class RealmAdminResource {
@Path("client-policies/policies") @Path("client-policies/policies")
public ClientPoliciesResource getClientPoliciesResource() { public ClientPoliciesResource getClientPoliciesResource() {
ProfileHelper.requireFeature(Profile.Feature.CLIENT_POLICIES);
ClientPoliciesResource resource = new ClientPoliciesResource(realm, auth); ClientPoliciesResource resource = new ClientPoliciesResource(realm, auth);
ResteasyProviderFactory.getInstance().injectProperties(resource); ResteasyProviderFactory.getInstance().injectProperties(resource);
return resource; return resource;
@ -1216,6 +1219,7 @@ public class RealmAdminResource {
@Path("client-policies/profiles") @Path("client-policies/profiles")
public ClientProfilesResource getClientProfilesResource() { public ClientProfilesResource getClientProfilesResource() {
ProfileHelper.requireFeature(Profile.Feature.CLIENT_POLICIES);
ClientProfilesResource resource = new ClientProfilesResource(realm, auth); ClientProfilesResource resource = new ClientProfilesResource(realm, auth);
ResteasyProviderFactory.getInstance().injectProperties(resource); ResteasyProviderFactory.getInstance().injectProperties(resource);
return resource; return resource;

View file

@ -102,7 +102,7 @@ public class RealmsAdminResource {
protected RealmRepresentation toRealmRep(RealmModel realm) { protected RealmRepresentation toRealmRep(RealmModel realm) {
if (AdminPermissions.realms(session, auth).canView(realm)) { if (AdminPermissions.realms(session, auth).canView(realm)) {
return ModelToRepresentation.toRepresentation(realm, false); return ModelToRepresentation.toRepresentation(session, realm, false);
} else if (AdminPermissions.realms(session, auth).isAdmin(realm)) { } else if (AdminPermissions.realms(session, auth).isAdmin(realm)) {
RealmRepresentation rep = new RealmRepresentation(); RealmRepresentation rep = new RealmRepresentation();
rep.setRealm(realm.getName()); rep.setRealm(realm.getName());

View file

@ -0,0 +1,18 @@
#
# Copyright 2021 Red Hat, Inc. and/or its affiliates
# and other contributors as indicated by the @author tags.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
org.keycloak.services.clientpolicy.DefaultClientPolicyManagerFactory

View file

@ -1,18 +0,0 @@
{
"policies": [
{
"name": "builtin-default-policy",
"description": "The built-in default policy applied to all clients.",
"builtin": true,
"enable": false,
"conditions": [
{
"anyclient-condition": {}
}
],
"profiles": [
"builtin-default-profile"
]
}
]
}

View file

@ -1,12 +1,12 @@
{ {
"profiles": [ "profiles": [
{ {
"name": "builtin-default-profile", "name": "global-default-profile",
"description": "The built-in default profile for enforcing basic security level to clients.", "description": "The global default profile for enforcing basic security level to clients.",
"builtin": true,
"executors": [ "executors": [
{ {
"secure-session-enforce-executor": {} "executor": "secure-session-enforce-executor",
"configuration": {}
} }
] ]
} }

View file

@ -21,7 +21,7 @@ public class RunHelpers {
@Override @Override
public FetchOnServer getRunOnServer() { public FetchOnServer getRunOnServer() {
return (FetchOnServer) session -> ModelToRepresentation.toRepresentation(session.getContext().getRealm(), true); return (FetchOnServer) session -> ModelToRepresentation.toRepresentation(session, session.getContext().getRealm(), true);
} }
@Override @Override

View file

@ -22,23 +22,16 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyVote; import org.keycloak.services.clientpolicy.ClientPolicyVote;
import org.keycloak.services.clientpolicy.condition.ClientPolicyConditionConfiguration; import org.keycloak.services.clientpolicy.condition.AbstractClientPolicyConditionProvider;
import org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProvider; import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
*/ */
public class TestRaiseExeptionCondition implements ClientPolicyConditionProvider<TestRaiseExeptionCondition.Configuration> { public class TestRaiseExeptionCondition extends AbstractClientPolicyConditionProvider<TestRaiseExeptionCondition.Configuration> {
// to avoid null configuration, use vacant new instance to indicate that there is no configuration set up.
private Configuration configuration = new Configuration();
public TestRaiseExeptionCondition(KeycloakSession session) { public TestRaiseExeptionCondition(KeycloakSession session) {
} super(session);
@Override
public void setupConfiguration(Configuration config) {
this.configuration = config;
} }
@Override @Override
@ -46,7 +39,7 @@ public class TestRaiseExeptionCondition implements ClientPolicyConditionProvider
return Configuration.class; return Configuration.class;
} }
public static class Configuration extends ClientPolicyConditionConfiguration { public static class Configuration extends ClientPolicyConditionConfigurationRepresentation {
} }
@Override @Override

View file

@ -66,4 +66,8 @@ public class TestRaiseExeptionConditionFactory implements ClientPolicyConditionP
return Collections.emptyList(); return Collections.emptyList();
} }
@Override
public boolean isSupported() {
return true;
}
} }

View file

@ -22,10 +22,10 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyEvent; import org.keycloak.services.clientpolicy.ClientPolicyEvent;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorConfiguration; import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider; import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider;
public class TestRaiseExeptionExecutor implements ClientPolicyExecutorProvider<ClientPolicyExecutorConfiguration> { public class TestRaiseExeptionExecutor implements ClientPolicyExecutorProvider<ClientPolicyExecutorConfigurationRepresentation> {
private static final Logger logger = Logger.getLogger(TestRaiseExeptionExecutor.class); private static final Logger logger = Logger.getLogger(TestRaiseExeptionExecutor.class);

View file

@ -63,4 +63,8 @@ public class TestRaiseExeptionExecutorFactory implements ClientPolicyExecutorPro
return Collections.emptyList(); return Collections.emptyList();
} }
@Override
public boolean isSupported() {
return true;
}
} }

View file

@ -17,6 +17,7 @@
package org.keycloak.testsuite.client; package org.keycloak.testsuite.client;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@ -38,6 +39,7 @@ import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
@ -60,9 +62,11 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicNameValuePair;
import org.hamcrest.Matchers;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule; import org.junit.Rule;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException; import org.keycloak.OAuthErrorException;
@ -74,6 +78,7 @@ import org.keycloak.client.registration.ClientRegistration;
import org.keycloak.client.registration.ClientRegistrationException; import org.keycloak.client.registration.ClientRegistrationException;
import org.keycloak.common.util.Base64; import org.keycloak.common.util.Base64;
import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.BouncyIntegration;
import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.KeycloakUriBuilder; import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.common.util.Time; import org.keycloak.common.util.Time;
@ -92,6 +97,10 @@ import org.keycloak.representations.JsonWebToken;
import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation; import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
import org.keycloak.representations.idm.ClientInitialAccessPresentation; import org.keycloak.representations.idm.ClientInitialAccessPresentation;
import org.keycloak.representations.idm.ClientPoliciesRepresentation; import org.keycloak.representations.idm.ClientPoliciesRepresentation;
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
import org.keycloak.representations.idm.ClientPolicyConditionRepresentation;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.representations.idm.ClientPolicyExecutorRepresentation;
import org.keycloak.representations.idm.ClientPolicyRepresentation; import org.keycloak.representations.idm.ClientPolicyRepresentation;
import org.keycloak.representations.idm.ClientProfileRepresentation; import org.keycloak.representations.idm.ClientProfileRepresentation;
import org.keycloak.representations.idm.ClientProfilesRepresentation; import org.keycloak.representations.idm.ClientProfilesRepresentation;
@ -100,7 +109,6 @@ import org.keycloak.representations.oidc.OIDCClientRepresentation;
import org.keycloak.representations.oidc.TokenMetadataRepresentation; import org.keycloak.representations.oidc.TokenMetadataRepresentation;
import org.keycloak.services.Urls; import org.keycloak.services.Urls;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.condition.AnyClientCondition;
import org.keycloak.services.clientpolicy.condition.AnyClientConditionFactory; import org.keycloak.services.clientpolicy.condition.AnyClientConditionFactory;
import org.keycloak.services.clientpolicy.condition.ClientAccessTypeCondition; import org.keycloak.services.clientpolicy.condition.ClientAccessTypeCondition;
import org.keycloak.services.clientpolicy.condition.ClientAccessTypeConditionFactory; import org.keycloak.services.clientpolicy.condition.ClientAccessTypeConditionFactory;
@ -132,6 +140,7 @@ import org.keycloak.services.clientpolicy.executor.SecureSigningAlgorithmEnforce
import org.keycloak.services.clientpolicy.executor.SecureSigningAlgorithmForSignedJwtEnforceExecutor; import org.keycloak.services.clientpolicy.executor.SecureSigningAlgorithmForSignedJwtEnforceExecutor;
import org.keycloak.services.clientpolicy.executor.SecureSigningAlgorithmForSignedJwtEnforceExecutorFactory; import org.keycloak.services.clientpolicy.executor.SecureSigningAlgorithmForSignedJwtEnforceExecutorFactory;
import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls; import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls;
@ -170,6 +179,11 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
private static final ObjectMapper objectMapper = new ObjectMapper(); private static final ObjectMapper objectMapper = new ObjectMapper();
@BeforeClass
public static void beforeClientPoliciesTest() {
BouncyIntegration.init();
}
@Rule @Rule
public AssertEvents events = new AssertEvents(this); public AssertEvents events = new AssertEvents(this);
@ -189,9 +203,9 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
revertToBuiltinPolicies(); revertToBuiltinPolicies();
} }
protected void loadValidProfilesAndPolicies() throws Exception { protected void setupValidProfilesAndPolicies() throws Exception {
// load profiles // load profiles
ClientProfileRepresentation loadedProfileRep = (new ClientProfileBuilder()).createProfile("ordinal-test-profile", "The profile that can be loaded.", Boolean.FALSE, null) ClientProfileRepresentation loadedProfileRep = (new ClientProfileBuilder()).createProfile("ordinal-test-profile", "The profile that can be loaded.")
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID,
createSecureClientAuthEnforceExecutorConfig( createSecureClientAuthEnforceExecutorConfig(
Boolean.TRUE, Boolean.TRUE,
@ -199,7 +213,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
JWTClientAuthenticator.PROVIDER_ID)) JWTClientAuthenticator.PROVIDER_ID))
.toRepresentation(); .toRepresentation();
ClientProfileRepresentation loadedProfileRepWithoutBuiltinField = (new ClientProfileBuilder()).createProfile("lack-of-builtin-field-test-profile", "Without builtin field that is treated as builtin=false.", null, null) ClientProfileRepresentation loadedProfileRepWithoutBuiltinField = (new ClientProfileBuilder()).createProfile("lack-of-builtin-field-test-profile", "Without builtin field that is treated as builtin=false.")
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID,
createSecureClientAuthEnforceExecutorConfig( createSecureClientAuthEnforceExecutorConfig(
Boolean.TRUE, Boolean.TRUE,
@ -226,26 +240,25 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
(new ClientPolicyBuilder()).createPolicy( (new ClientPolicyBuilder()).createPolicy(
"new-policy", "new-policy",
"duplicated profiles are ignored.", "duplicated profiles are ignored.",
Boolean.FALSE, Boolean.TRUE)
Boolean.TRUE,
null,
Arrays.asList("builtin-default-profile", "ordinal-test-profile", "lack-of-builtin-field-test-profile", "ordinal-test-profile"))
.addCondition(ClientAccessTypeConditionFactory.PROVIDER_ID, .addCondition(ClientAccessTypeConditionFactory.PROVIDER_ID,
createClientAccessTypeConditionConfig(Arrays.asList(ClientAccessTypeConditionFactory.TYPE_PUBLIC, ClientAccessTypeConditionFactory.TYPE_BEARERONLY))) createClientAccessTypeConditionConfig(Arrays.asList(ClientAccessTypeConditionFactory.TYPE_PUBLIC, ClientAccessTypeConditionFactory.TYPE_BEARERONLY)))
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE))) createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE)))
.addCondition(ClientScopesConditionFactory.PROVIDER_ID, .addCondition(ClientScopesConditionFactory.PROVIDER_ID,
createClientScopesConditionConfig(ClientScopesConditionFactory.OPTIONAL, Arrays.asList(SAMPLE_CLIENT_ROLE))) createClientScopesConditionConfig(ClientScopesConditionFactory.OPTIONAL, Arrays.asList(SAMPLE_CLIENT_ROLE)))
.addProfile("global-default-profile")
.addProfile("ordinal-test-profile")
.addProfile("lack-of-builtin-field-test-profile")
.addProfile("ordinal-test-profile")
.toRepresentation(); .toRepresentation();
ClientPolicyRepresentation loadedPolicyRepWithoutBuiltinField = ClientPolicyRepresentation loadedPolicyRepWithoutBuiltinField =
(new ClientPolicyBuilder()).createPolicy( (new ClientPolicyBuilder()).createPolicy(
"lack-of-builtin-field-test-policy", "lack-of-builtin-field-test-policy",
"Without builtin field that is treated as builtin=false.", "Without builtin field that is treated as builtin=false.",
null, null)
null,
null,
Arrays.asList("lack-of-builtin-field-test-profile"))
.addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID,
createClientUpdateContextConditionConfig(Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER))) createClientUpdateContextConditionConfig(Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER)))
.addCondition(ClientUpdateSourceGroupsConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateSourceGroupsConditionFactory.PROVIDER_ID,
@ -254,6 +267,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
createClientUpdateSourceHostsConditionConfig(Arrays.asList("localhost", "127.0.0.1"))) createClientUpdateSourceHostsConditionConfig(Arrays.asList("localhost", "127.0.0.1")))
.addCondition(ClientUpdateSourceRolesConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateSourceRolesConditionFactory.PROVIDER_ID,
createClientUpdateSourceRolesConditionConfig(Arrays.asList(AdminRoles.CREATE_CLIENT))) createClientUpdateSourceRolesConditionConfig(Arrays.asList(AdminRoles.CREATE_CLIENT)))
.addProfile("lack-of-builtin-field-test-profile")
.toRepresentation(); .toRepresentation();
json = (new ClientPoliciesBuilder()) json = (new ClientPoliciesBuilder())
@ -268,21 +282,21 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
protected void assertExpectedLoadedProfiles(Consumer<ClientProfilesRepresentation> modifiedAssertion) { protected void assertExpectedLoadedProfiles(Consumer<ClientProfilesRepresentation> modifiedAssertion) {
// retrieve loaded builtin profiles // retrieve loaded builtin profiles
ClientProfilesRepresentation actualProfilesRep = getProfiles(); ClientProfilesRepresentation actualProfilesRep = getProfilesWithGlobals();
// same profiles // same profiles
assertExpectedProfiles(actualProfilesRep, Arrays.asList("builtin-default-profile", "ordinal-test-profile", "lack-of-builtin-field-test-profile")); assertExpectedProfiles(actualProfilesRep, Arrays.asList("global-default-profile"), Arrays.asList("ordinal-test-profile", "lack-of-builtin-field-test-profile"));
// each profile - builtin-default-profile // each profile - global-default-profile
ClientProfileRepresentation actualProfileRep = getProfileRepresentation(actualProfilesRep, "builtin-default-profile"); ClientProfileRepresentation actualProfileRep = getProfileRepresentation(actualProfilesRep, "global-default-profile", true);
assertExpectedProfile(actualProfileRep, "builtin-default-profile", "The built-in default profile for enforcing basic security level to clients.", true); assertExpectedProfile(actualProfileRep, "global-default-profile", "The global default profile for enforcing basic security level to clients.");
// each executor // each executor
assertExpectedExecutors(Arrays.asList(SecureSessionEnforceExecutorFactory.PROVIDER_ID), actualProfileRep); assertExpectedExecutors(Arrays.asList(SecureSessionEnforceExecutorFactory.PROVIDER_ID), actualProfileRep);
assertExpectedSecureSessionEnforceExecutor(actualProfileRep); assertExpectedSecureSessionEnforceExecutor(actualProfileRep);
// each profile - ordinal-test-profile - updated // each profile - ordinal-test-profile - updated
actualProfileRep = getProfileRepresentation(actualProfilesRep, "ordinal-test-profile"); actualProfileRep = getProfileRepresentation(actualProfilesRep, "ordinal-test-profile", false);
modifiedAssertion.accept(actualProfilesRep); modifiedAssertion.accept(actualProfilesRep);
// each executor // each executor
@ -290,8 +304,8 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
assertExpectedSecureClientAuthEnforceExecutor(Arrays.asList(JWTClientAuthenticator.PROVIDER_ID), true, JWTClientAuthenticator.PROVIDER_ID, actualProfileRep); assertExpectedSecureClientAuthEnforceExecutor(Arrays.asList(JWTClientAuthenticator.PROVIDER_ID), true, JWTClientAuthenticator.PROVIDER_ID, actualProfileRep);
// each profile - lack-of-builtin-field-test-profile // each profile - lack-of-builtin-field-test-profile
actualProfileRep = getProfileRepresentation(actualProfilesRep, "lack-of-builtin-field-test-profile"); actualProfileRep = getProfileRepresentation(actualProfilesRep, "lack-of-builtin-field-test-profile", false);
assertExpectedProfile(actualProfileRep, "lack-of-builtin-field-test-profile", "Without builtin field that is treated as builtin=false.", false); assertExpectedProfile(actualProfileRep, "lack-of-builtin-field-test-profile", "Without builtin field that is treated as builtin=false.");
// each executor // each executor
assertExpectedExecutors(Arrays.asList( assertExpectedExecutors(Arrays.asList(
@ -319,7 +333,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
ClientPoliciesRepresentation actualPoliciesRep = getPolicies(); ClientPoliciesRepresentation actualPoliciesRep = getPolicies();
// same policies // same policies
assertExpectedPolicies(Arrays.asList("builtin-default-policy", "new-policy", "lack-of-builtin-field-test-policy"), actualPoliciesRep); assertExpectedPolicies(Arrays.asList("new-policy", "lack-of-builtin-field-test-policy"), actualPoliciesRep);
// each policy - new-policy - updated // each policy - new-policy - updated
ClientPolicyRepresentation actualPolicyRep = getPolicyRepresentation(actualPoliciesRep, "new-policy"); ClientPolicyRepresentation actualPolicyRep = getPolicyRepresentation(actualPoliciesRep, "new-policy");
@ -333,13 +347,13 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
// each policy - lack-of-builtin-field-test-policy // each policy - lack-of-builtin-field-test-policy
actualPolicyRep = getPolicyRepresentation(actualPoliciesRep, "lack-of-builtin-field-test-policy"); actualPolicyRep = getPolicyRepresentation(actualPoliciesRep, "lack-of-builtin-field-test-policy");
assertExpectedPolicy("lack-of-builtin-field-test-policy", "Without builtin field that is treated as builtin=false.", false, false, Arrays.asList("lack-of-builtin-field-test-profile"), actualPolicyRep); assertExpectedPolicy("lack-of-builtin-field-test-policy", "Without builtin field that is treated as builtin=false.", false, Arrays.asList("lack-of-builtin-field-test-profile"), actualPolicyRep);
// each condition // each condition
assertExpectedConditions(Arrays.asList(ClientUpdateContextConditionFactory.PROVIDER_ID, ClientUpdateSourceGroupsConditionFactory.PROVIDER_ID, ClientUpdateSourceHostsConditionFactory.PROVIDER_ID, ClientUpdateSourceRolesConditionFactory.PROVIDER_ID), actualPolicyRep); assertExpectedConditions(Arrays.asList(ClientUpdateContextConditionFactory.PROVIDER_ID, ClientUpdateSourceGroupsConditionFactory.PROVIDER_ID, ClientUpdateSourceHostsConditionFactory.PROVIDER_ID, ClientUpdateSourceRolesConditionFactory.PROVIDER_ID), actualPolicyRep);
assertExpectedClientUpdateContextCondition(Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER), actualPolicyRep); assertExpectedClientUpdateContextCondition(Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER), actualPolicyRep);
assertExpectedClientUpdateSourceGroupsCondition(Arrays.asList("topGroup"), actualPolicyRep); assertExpectedClientUpdateSourceGroupsCondition(Arrays.asList("topGroup"), actualPolicyRep);
assertExpectedClientUpdateSourceHostsCondition(Arrays.asList("localhost", "127.0.0.1"), Arrays.asList(Boolean.TRUE, Boolean.TRUE), actualPolicyRep); assertExpectedClientUpdateSourceHostsCondition(Arrays.asList("localhost", "127.0.0.1"), actualPolicyRep);
assertExpectedClientUpdateSourceRolesCondition(Arrays.asList(AdminRoles.CREATE_CLIENT), actualPolicyRep); assertExpectedClientUpdateSourceRolesCondition(Arrays.asList(AdminRoles.CREATE_CLIENT), actualPolicyRep);
} }
@ -783,51 +797,23 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
profileRep = new ClientProfileRepresentation(); profileRep = new ClientProfileRepresentation();
} }
public ClientProfileBuilder createProfile(String name, String description, Boolean isBuiltin, List<Object> executors) { public ClientProfileBuilder createProfile(String name, String description) {
if (name != null) { if (name != null) {
profileRep.setName(name); profileRep.setName(name);
} }
if (description != null) { if (description != null) {
profileRep.setDescription(description); profileRep.setDescription(description);
} }
if (isBuiltin != null) {
profileRep.setBuiltin(isBuiltin);
} else {
profileRep.setBuiltin(Boolean.FALSE);
}
if (executors != null) {
profileRep.setExecutors(executors);
} else {
profileRep.setExecutors(new ArrayList<>()); profileRep.setExecutors(new ArrayList<>());
}
return this; return this;
} }
public ClientProfileBuilder addExecutor(String providerId, Object config) { public ClientProfileBuilder addExecutor(String providerId, ClientPolicyExecutorConfigurationRepresentation config) {
String configString = null;
if (config == null) { if (config == null) {
configString = "{}"; config = new ClientPolicyExecutorConfigurationRepresentation();
} else {
try {
configString = objectMapper.writeValueAsString(config);
} catch (JsonProcessingException e) {
fail();
} }
} profileRep.getExecutors().add(new ClientPolicyExecutorRepresentation(providerId, config));
String executorJson = (new StringBuilder())
.append("{\"")
.append(providerId)
.append("\":")
.append(configString)
.append("}")
.toString();
JsonNode node = null;
try {
node = objectMapper.readTree(executorJson);
} catch (JsonProcessingException e) {
fail();
}
profileRep.getExecutors().add(node);
return this; return this;
} }
@ -849,19 +835,19 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
// Client Profiles - Executor CRUD Operations // Client Profiles - Executor CRUD Operations
protected Object createHolderOfKeyEnforceExecutorConfig(Boolean isAugment) { protected HolderOfKeyEnforceExecutor.Configuration createHolderOfKeyEnforceExecutorConfig(Boolean isAugment) {
HolderOfKeyEnforceExecutor.Configuration config = new HolderOfKeyEnforceExecutor.Configuration(); HolderOfKeyEnforceExecutor.Configuration config = new HolderOfKeyEnforceExecutor.Configuration();
config.setAugment(isAugment); config.setAugment(isAugment);
return config; return config;
} }
protected Object createPKCEEnforceExecutorConfig(Boolean isAugment) { protected PKCEEnforceExecutor.Configuration createPKCEEnforceExecutorConfig(Boolean isAugment) {
PKCEEnforceExecutor.Configuration config = new PKCEEnforceExecutor.Configuration(); PKCEEnforceExecutor.Configuration config = new PKCEEnforceExecutor.Configuration();
config.setAugment(isAugment); config.setAugment(isAugment);
return config; return config;
} }
protected Object createSecureClientAuthEnforceExecutorConfig(Boolean isAugment, List<String> clientAuthns, String clientAuthnsAugment) { protected SecureClientAuthEnforceExecutor.Configuration createSecureClientAuthEnforceExecutorConfig(Boolean isAugment, List<String> clientAuthns, String clientAuthnsAugment) {
SecureClientAuthEnforceExecutor.Configuration config = new SecureClientAuthEnforceExecutor.Configuration(); SecureClientAuthEnforceExecutor.Configuration config = new SecureClientAuthEnforceExecutor.Configuration();
config.setAugment(isAugment); config.setAugment(isAugment);
config.setClientAuthns(clientAuthns); config.setClientAuthns(clientAuthns);
@ -869,20 +855,20 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
return config; return config;
} }
protected Object createSecureRequestObjectExecutorConfig(Integer availablePeriod, Boolean verifyNbf) { protected SecureRequestObjectExecutor.Configuration createSecureRequestObjectExecutorConfig(Integer availablePeriod, Boolean verifyNbf) {
SecureRequestObjectExecutor.Configuration config = new SecureRequestObjectExecutor.Configuration(); SecureRequestObjectExecutor.Configuration config = new SecureRequestObjectExecutor.Configuration();
if (availablePeriod != null) config.setAvailablePeriod(availablePeriod); if (availablePeriod != null) config.setAvailablePeriod(availablePeriod);
if (verifyNbf != null) config.setVerifyNbf(verifyNbf); if (verifyNbf != null) config.setVerifyNbf(verifyNbf);
return config; return config;
} }
protected Object createSecureSigningAlgorithmForSignedJwtEnforceExecutorConfig(Boolean requireClientAssertion) { protected SecureSigningAlgorithmForSignedJwtEnforceExecutor.Configuration createSecureSigningAlgorithmForSignedJwtEnforceExecutorConfig(Boolean requireClientAssertion) {
SecureSigningAlgorithmForSignedJwtEnforceExecutor.Configuration config = new SecureSigningAlgorithmForSignedJwtEnforceExecutor.Configuration(); SecureSigningAlgorithmForSignedJwtEnforceExecutor.Configuration config = new SecureSigningAlgorithmForSignedJwtEnforceExecutor.Configuration();
config.setRequireClientAssertion(requireClientAssertion); config.setRequireClientAssertion(requireClientAssertion);
return config; return config;
} }
protected Object createSecureSigningAlgorithmEnforceExecutorConfig(String defaultAlgorithm) { protected SecureSigningAlgorithmEnforceExecutor.Configuration createSecureSigningAlgorithmEnforceExecutorConfig(String defaultAlgorithm) {
SecureSigningAlgorithmEnforceExecutor.Configuration config = new SecureSigningAlgorithmEnforceExecutor.Configuration(); SecureSigningAlgorithmEnforceExecutor.Configuration config = new SecureSigningAlgorithmEnforceExecutor.Configuration();
config.setDefaultAlgorithm(defaultAlgorithm); config.setDefaultAlgorithm(defaultAlgorithm);
return config; return config;
@ -927,59 +913,25 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
policyRep = new ClientPolicyRepresentation(); policyRep = new ClientPolicyRepresentation();
} }
public ClientPolicyBuilder createPolicy(String name, String description, Boolean isBuiltin, Boolean isEnabled, List<Object> conditions, List<String> profiles) { public ClientPolicyBuilder createPolicy(String name, String description, Boolean isEnabled) {
policyRep.setName(name); policyRep.setName(name);
if (description != null) { if (description != null) {
policyRep.setDescription(description); policyRep.setDescription(description);
} }
if (isBuiltin != null) {
policyRep.setBuiltin(isBuiltin);
} else {
policyRep.setBuiltin(Boolean.FALSE);
}
if (isEnabled != null) { if (isEnabled != null) {
policyRep.setEnable(isEnabled); policyRep.setEnabled(isEnabled);
} else { } else {
policyRep.setEnable(Boolean.FALSE); policyRep.setEnabled(Boolean.FALSE);
} }
if (conditions != null) {
policyRep.setConditions(conditions);
} else {
policyRep.setConditions(new ArrayList<>()); policyRep.setConditions(new ArrayList<>());
}
if (profiles != null) {
policyRep.setProfiles(profiles);
} else {
policyRep.setProfiles(new ArrayList<>()); policyRep.setProfiles(new ArrayList<>());
}
return this; return this;
} }
public ClientPolicyBuilder addCondition(String providerId, Object config) { public ClientPolicyBuilder addCondition(String providerId, ClientPolicyConditionConfigurationRepresentation config) {
String configString = null; policyRep.getConditions().add(new ClientPolicyConditionRepresentation(providerId, config));
if (config == null) {
configString = "{}";
} else {
try {
configString = objectMapper.writeValueAsString(config);
} catch (JsonProcessingException e) {
fail();
}
}
String conditionJson = (new StringBuilder())
.append("{\"")
.append(providerId)
.append("\":")
.append(configString)
.append("}")
.toString();
JsonNode node = null;
try {
node = objectMapper.readTree(conditionJson);
} catch (JsonProcessingException e) {
fail();
}
policyRep.getConditions().add(node);
return this; return this;
} }
@ -1005,58 +957,58 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
// Client Policies - Condition CRUD Operations // Client Policies - Condition CRUD Operations
protected Object createTestRaiseExeptionConditionConfig() { protected TestRaiseExeptionCondition.Configuration createTestRaiseExeptionConditionConfig() {
return new TestRaiseExeptionCondition.Configuration(); return new TestRaiseExeptionCondition.Configuration();
} }
protected Object createAnyClientConditionConfig() { protected ClientPolicyConditionConfigurationRepresentation createAnyClientConditionConfig() {
return new AnyClientCondition.Configuration(); return new ClientPolicyConditionConfigurationRepresentation();
} }
protected Object createAnyClientConditionConfig(Boolean isNegativeLogic) { protected ClientPolicyConditionConfigurationRepresentation createAnyClientConditionConfig(Boolean isNegativeLogic) {
AnyClientCondition.Configuration config = new AnyClientCondition.Configuration(); ClientPolicyConditionConfigurationRepresentation config = new ClientPolicyConditionConfigurationRepresentation();
config.setNegativeLogic(isNegativeLogic); config.setNegativeLogic(isNegativeLogic);
return config; return config;
} }
protected Object createClientAccessTypeConditionConfig(List<String> types) { protected ClientAccessTypeCondition.Configuration createClientAccessTypeConditionConfig(List<String> types) {
ClientAccessTypeCondition.Configuration config = new ClientAccessTypeCondition.Configuration(); ClientAccessTypeCondition.Configuration config = new ClientAccessTypeCondition.Configuration();
config.setType(types); config.setType(types);
return config; return config;
} }
protected Object createClientRolesConditionConfig(List<String> roles) { protected ClientRolesCondition.Configuration createClientRolesConditionConfig(List<String> roles) {
ClientRolesCondition.Configuration config = new ClientRolesCondition.Configuration(); ClientRolesCondition.Configuration config = new ClientRolesCondition.Configuration();
config.setRoles(roles); config.setRoles(roles);
return config; return config;
} }
protected Object createClientScopesConditionConfig(String type, List<String> scopes) { protected ClientScopesCondition.Configuration createClientScopesConditionConfig(String type, List<String> scopes) {
ClientScopesCondition.Configuration config = new ClientScopesCondition.Configuration(); ClientScopesCondition.Configuration config = new ClientScopesCondition.Configuration();
config.setType(type); config.setType(type);
config.setScope(scopes); config.setScope(scopes);
return config; return config;
} }
protected Object createClientUpdateContextConditionConfig(List<String> updateClientSource) { protected ClientUpdateContextCondition.Configuration createClientUpdateContextConditionConfig(List<String> updateClientSource) {
ClientUpdateContextCondition.Configuration config = new ClientUpdateContextCondition.Configuration(); ClientUpdateContextCondition.Configuration config = new ClientUpdateContextCondition.Configuration();
config.setUpdateClientSource(updateClientSource); config.setUpdateClientSource(updateClientSource);
return config; return config;
} }
protected Object createClientUpdateSourceGroupsConditionConfig(List<String> groups) { protected ClientUpdateSourceGroupsCondition.Configuration createClientUpdateSourceGroupsConditionConfig(List<String> groups) {
ClientUpdateSourceGroupsCondition.Configuration config = new ClientUpdateSourceGroupsCondition.Configuration(); ClientUpdateSourceGroupsCondition.Configuration config = new ClientUpdateSourceGroupsCondition.Configuration();
config.setGroups(groups); config.setGroups(groups);
return config; return config;
} }
protected Object createClientUpdateSourceHostsConditionConfig(List<String> trustedHosts) { protected ClientUpdateSourceHostsCondition.Configuration createClientUpdateSourceHostsConditionConfig(List<String> trustedHosts) {
ClientUpdateSourceHostsCondition.Configuration config = new ClientUpdateSourceHostsCondition.Configuration(); ClientUpdateSourceHostsCondition.Configuration config = new ClientUpdateSourceHostsCondition.Configuration();
config.setTrustedHosts(trustedHosts); config.setTrustedHosts(trustedHosts);
return config; return config;
} }
protected Object createClientUpdateSourceRolesConditionConfig(List<String> roles) { protected ClientUpdateSourceRolesCondition.Configuration createClientUpdateSourceRolesConditionConfig(List<String> roles) {
ClientUpdateSourceRolesCondition.Configuration config = new ClientUpdateSourceRolesCondition.Configuration(); ClientUpdateSourceRolesCondition.Configuration config = new ClientUpdateSourceRolesCondition.Configuration();
config.setRoles(roles); config.setRoles(roles);
return config; return config;
@ -1074,24 +1026,15 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
return json; return json;
} }
protected ClientProfilesRepresentation convertToProfiles(String json) { // TODO: Possibly change this to accept ClientProfilesRepresentation instead of String to have more type-safety.
ClientProfilesRepresentation reps = null;
try {
reps = JsonSerialization.readValue(json, ClientProfilesRepresentation.class);
} catch (IOException e) {
fail();
}
return reps;
}
protected String getProfilesJson() {
return adminClient.realm(REALM_NAME).clientPoliciesProfilesResource().getProfiles();
}
protected void updateProfiles(String json) throws ClientPolicyException { protected void updateProfiles(String json) throws ClientPolicyException {
Response resp = adminClient.realm(REALM_NAME).clientPoliciesProfilesResource().updateProfiles(json); try {
if (resp.getStatus() != 204) { ClientProfilesRepresentation clientProfiles = JsonSerialization.readValue(json, ClientProfilesRepresentation.class);
throw new ClientPolicyException("update profiles failed", resp.getStatusInfo().toString()); adminClient.realm(REALM_NAME).clientPoliciesProfilesResource().updateProfiles(clientProfiles);
} catch (BadRequestException e) {
throw new ClientPolicyException("update profiles failed", e.getResponse().getStatusInfo().toString());
} catch (Exception e) {
throw new ClientPolicyException("update profiles failed", e.getMessage());
} }
} }
@ -1103,16 +1046,12 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
updateProfiles("{}"); updateProfiles("{}");
} }
protected ClientProfilesRepresentation getProfiles() { protected ClientProfilesRepresentation getProfilesWithGlobals() {
return convertToProfiles(getProfilesJson()); return adminClient.realm(REALM_NAME).clientPoliciesProfilesResource().getProfiles(true);
} }
protected ClientProfilesRepresentation getProfilesWithoutBuiltin() { protected ClientProfilesRepresentation getProfilesWithoutGlobals() {
ClientProfilesRepresentation reps = new ClientProfilesRepresentation(); return adminClient.realm(REALM_NAME).clientPoliciesProfilesResource().getProfiles(false);
reps.setProfiles(new ArrayList<>());
ClientProfilesRepresentation repsWithBuiltin = getProfiles();
repsWithBuiltin.getProfiles().stream().filter(i->!i.isBuiltin()).forEach(j->reps.getProfiles().add(j));
return reps;
} }
protected String convertToProfileJson(ClientProfileRepresentation rep) { protected String convertToProfileJson(ClientProfileRepresentation rep) {
@ -1138,7 +1077,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
protected ClientProfileRepresentation getProfile(String name) { protected ClientProfileRepresentation getProfile(String name) {
if (name == null) return null; if (name == null) return null;
ClientProfilesRepresentation reps = getProfiles(); ClientProfilesRepresentation reps = getProfilesWithGlobals();
if (reps == null || reps.getProfiles() == null) return null; if (reps == null || reps.getProfiles() == null) return null;
if (reps.getProfiles().stream().anyMatch(i->name.equals(i.getName()))) { if (reps.getProfiles().stream().anyMatch(i->name.equals(i.getName()))) {
@ -1153,7 +1092,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
} }
protected void addProfile(ClientProfileRepresentation profileRep) throws ClientPolicyException { protected void addProfile(ClientProfileRepresentation profileRep) throws ClientPolicyException {
ClientProfilesRepresentation reps = getProfilesWithoutBuiltin(); ClientProfilesRepresentation reps = getProfilesWithoutGlobals();
if (reps == null || reps.getProfiles() == null) return; if (reps == null || reps.getProfiles() == null) return;
reps.getProfiles().add(profileRep); reps.getProfiles().add(profileRep);
updateProfiles(convertToProfilesJson(reps)); updateProfiles(convertToProfilesJson(reps));
@ -1164,7 +1103,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
if (profileRep == null || profileRep.getName() == null) return; if (profileRep == null || profileRep.getName() == null) return;
String profileName = profileRep.getName(); String profileName = profileRep.getName();
ClientProfilesRepresentation reps = getProfilesWithoutBuiltin(); ClientProfilesRepresentation reps = getProfilesWithoutGlobals();
if (reps.getProfiles().stream().anyMatch(i->profileName.equals(i.getName()))) { if (reps.getProfiles().stream().anyMatch(i->profileName.equals(i.getName()))) {
ClientProfileRepresentation rep = reps.getProfiles().stream().filter(i->profileName.equals(i.getName())).collect(Collectors.toList()).get(0); ClientProfileRepresentation rep = reps.getProfiles().stream().filter(i->profileName.equals(i.getName())).collect(Collectors.toList()).get(0);
@ -1179,7 +1118,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
protected void deleteProfile(String profileName) throws ClientPolicyException { protected void deleteProfile(String profileName) throws ClientPolicyException {
if (profileName == null) return; if (profileName == null) return;
ClientProfilesRepresentation reps = getProfilesWithoutBuiltin(); ClientProfilesRepresentation reps = getProfilesWithoutGlobals();
if (reps.getProfiles().stream().anyMatch(i->profileName.equals(i.getName()))) { if (reps.getProfiles().stream().anyMatch(i->profileName.equals(i.getName()))) {
ClientProfileRepresentation rep = reps.getProfiles().stream().filter(i->profileName.equals(i.getName())).collect(Collectors.toList()).get(0); ClientProfileRepresentation rep = reps.getProfiles().stream().filter(i->profileName.equals(i.getName())).collect(Collectors.toList()).get(0);
@ -1202,45 +1141,24 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
return json; return json;
} }
protected ClientPoliciesRepresentation convertToPolicies(String json) { // TODO: Possibly change this to accept ClientPoliciesRepresentation instead of String to have more type-safety.
ClientPoliciesRepresentation reps = null;
try {
reps = JsonSerialization.readValue(json, ClientPoliciesRepresentation.class);
} catch (IOException e) {
fail();
}
return reps;
}
protected String getPoliciesJson() {
return adminClient.realm(REALM_NAME).clientPoliciesPoliciesResource().getPolicies();
}
protected void updatePolicies(String json) throws ClientPolicyException { protected void updatePolicies(String json) throws ClientPolicyException {
Response resp = adminClient.realm(REALM_NAME).clientPoliciesPoliciesResource().updatePolicies(json); try {
if (resp.getStatus() != 204) { ClientPoliciesRepresentation clientPolicies = json==null ? null : JsonSerialization.readValue(json, ClientPoliciesRepresentation.class);
throw new ClientPolicyException("update profiles failed", resp.getStatusInfo().toString()); adminClient.realm(REALM_NAME).clientPoliciesPoliciesResource().updatePolicies(clientPolicies);
} catch (BadRequestException e) {
throw new ClientPolicyException("update policies failed", e.getResponse().getStatusInfo().toString());
} catch (IOException e) {
throw new ClientPolicyException("update policies failed", e.getMessage());
} }
} }
protected void updatePolicies(ClientPoliciesRepresentation reps) throws ClientPolicyException {
updatePolicies(convertToPoliciesJson(reps));
}
protected void revertToBuiltinPolicies() throws ClientPolicyException { protected void revertToBuiltinPolicies() throws ClientPolicyException {
updatePolicies("{}"); updatePolicies("{}");
} }
protected ClientPoliciesRepresentation getPolicies() { protected ClientPoliciesRepresentation getPolicies() {
return convertToPolicies(getPoliciesJson()); return adminClient.realm(REALM_NAME).clientPoliciesPoliciesResource().getPolicies();
}
protected ClientPoliciesRepresentation getPoliciesWithoutBuiltin() {
ClientPoliciesRepresentation reps = new ClientPoliciesRepresentation();
reps.setPolicies(new ArrayList<>());
ClientPoliciesRepresentation repsWithBuiltin = getPolicies();
repsWithBuiltin.getPolicies().stream().filter(i->!i.isBuiltin()).forEach(j->reps.getPolicies().add(j));
return reps;
} }
protected String convertToPolicyJson(ClientPolicyRepresentation rep) { protected String convertToPolicyJson(ClientPolicyRepresentation rep) {
@ -1281,7 +1199,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
} }
protected void addPolicy(ClientPolicyRepresentation policyRep) throws ClientPolicyException { protected void addPolicy(ClientPolicyRepresentation policyRep) throws ClientPolicyException {
ClientPoliciesRepresentation reps = getPoliciesWithoutBuiltin(); ClientPoliciesRepresentation reps = getPolicies();
if (reps == null || reps.getPolicies() == null) return; if (reps == null || reps.getPolicies() == null) return;
reps.getPolicies().add(policyRep); reps.getPolicies().add(policyRep);
updatePolicies(convertToPoliciesJson(reps)); updatePolicies(convertToPoliciesJson(reps));
@ -1292,7 +1210,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
if (policyRep == null || policyRep.getName() == null) return; if (policyRep == null || policyRep.getName() == null) return;
String policyName = policyRep.getName(); String policyName = policyRep.getName();
ClientPoliciesRepresentation reps = getPoliciesWithoutBuiltin(); ClientPoliciesRepresentation reps = getPolicies();
if (reps.getPolicies().stream().anyMatch(i->policyName.equals(i.getName()))) { if (reps.getPolicies().stream().anyMatch(i->policyName.equals(i.getName()))) {
ClientPolicyRepresentation rep = reps.getPolicies().stream().filter(i->policyName.equals(i.getName())).collect(Collectors.toList()).get(0); ClientPolicyRepresentation rep = reps.getPolicies().stream().filter(i->policyName.equals(i.getName())).collect(Collectors.toList()).get(0);
@ -1307,7 +1225,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
protected void deletePolicy(String policyName) throws ClientPolicyException { protected void deletePolicy(String policyName) throws ClientPolicyException {
if (policyName == null) return; if (policyName == null) return;
ClientPoliciesRepresentation reps = getPoliciesWithoutBuiltin(); ClientPoliciesRepresentation reps = getPolicies();
if (reps.getPolicies().stream().anyMatch(i->policyName.equals(i.getName()))) { if (reps.getPolicies().stream().anyMatch(i->policyName.equals(i.getName()))) {
ClientPolicyRepresentation rep = reps.getPolicies().stream().filter(i->policyName.equals(i.getName())).collect(Collectors.toList()).get(0); ClientPolicyRepresentation rep = reps.getPolicies().stream().filter(i->policyName.equals(i.getName())).collect(Collectors.toList()).get(0);
@ -1328,24 +1246,28 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
// profile // profile
protected ClientProfileRepresentation getProfileRepresentation(ClientProfilesRepresentation profilesRep, String name) { protected ClientProfileRepresentation getProfileRepresentation(ClientProfilesRepresentation profilesRep, String name, boolean global) {
return getCompoundRepresentation(profilesRep, name, (ClientProfilesRepresentation i)->i.getProfiles(), (ClientProfileRepresentation i)->i.getName()); Function<ClientProfilesRepresentation, List<ClientProfileRepresentation>> profilesListGetter = global ? ClientProfilesRepresentation::getGlobalProfiles : ClientProfilesRepresentation::getProfiles;
return getCompoundRepresentation(profilesRep, name, profilesListGetter, (ClientProfileRepresentation i)->i.getName());
} }
protected void assertExpectedProfiles(ClientProfilesRepresentation profilesRep, List<String> expectedProfiles) { protected void assertExpectedProfiles(ClientProfilesRepresentation profilesRep, List<String> expectedGlobalProfiles, List<String> expectedRealmProfiles) {
assertExpetedCompounds(expectedProfiles, profilesRep, (ClientProfilesRepresentation i)->i.getProfiles(), (ClientProfileRepresentation i)->i.getName()); assertExpectedCompounds(expectedGlobalProfiles, profilesRep, (ClientProfilesRepresentation i)->i.getGlobalProfiles(), (ClientProfileRepresentation i)->i.getName());
assertExpectedCompounds(expectedRealmProfiles, profilesRep, (ClientProfilesRepresentation i)->i.getProfiles(), (ClientProfileRepresentation i)->i.getName());
} }
protected void assertExpectedProfile(ClientProfileRepresentation actualProfileRep, String name, String description, boolean isBuiltin) { protected void assertExpectedProfile(ClientProfileRepresentation actualProfileRep, String name, String description) {
assertNotNull(actualProfileRep); assertNotNull(actualProfileRep);
assertEquals(description, actualProfileRep.getDescription()); assertEquals(description, actualProfileRep.getDescription());
assertEquals(isBuiltin, actualProfileRep.isBuiltin());
} }
// executors // executors
protected void assertExpectedExecutors(List<String> expectedExecutors, ClientProfileRepresentation profileRep) { protected void assertExpectedExecutors(List<String> expectedExecutors, ClientProfileRepresentation profileRep) {
assertExpetedElement(expectedExecutors, profileRep, (ClientProfileRepresentation i)->i.getExecutors()); List<String> actualExecutorNames = profileRep.getExecutors().stream()
.map(ClientPolicyExecutorRepresentation::getExecutorProviderId)
.collect(Collectors.toList());
assertThat(actualExecutorNames, Matchers.containsInAnyOrder(expectedExecutors.toArray()));
} }
protected void assertExpectedHolderOfKeyEnforceExecutor(boolean isAugment, ClientProfileRepresentation profileRep) { protected void assertExpectedHolderOfKeyEnforceExecutor(boolean isAugment, ClientProfileRepresentation profileRep) {
@ -1357,51 +1279,55 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
} }
protected void assertExpectedSecureClientAuthEnforceExecutor(List<String> clientAuthns, boolean isAugment, String clientAuthnsAugment, ClientProfileRepresentation profileRep) { protected void assertExpectedSecureClientAuthEnforceExecutor(List<String> clientAuthns, boolean isAugment, String clientAuthnsAugment, ClientProfileRepresentation profileRep) {
JsonNode actualExecutorConfig = assertExpectedAugmenedExecutor(isAugment, SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, profileRep); assertExpectedAugmenedExecutor(isAugment, SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, profileRep);
assertNotNull(profileRep);
Map<String, Object> actualExecutorConfig = getConfigOfExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, profileRep);
assertNotNull(actualExecutorConfig);
Set<String> actualClientAuthns = new HashSet<>(); Set<String> actualClientAuthns = new HashSet<>((Collection<String>) actualExecutorConfig.get("client-authns"));
if (actualExecutorConfig.findValue("client-authns") != null) actualExecutorConfig.findValue("client-authns").elements().forEachRemaining(i->actualClientAuthns.add(i.asText()));
assertEquals(new HashSet<>(clientAuthns), actualClientAuthns); assertEquals(new HashSet<>(clientAuthns), actualClientAuthns);
String actualClientAuthnAugment = null; String actualClientAuthnAugment = actualExecutorConfig.get("client-authns-augment").toString();
if (actualExecutorConfig.findValue("client-authns-augment") != null) actualClientAuthnAugment = actualExecutorConfig.findValue("client-authns-augment").asText();
assertEquals(clientAuthnsAugment, actualClientAuthnAugment); assertEquals(clientAuthnsAugment, actualClientAuthnAugment);
} }
protected void assertExpectedSecureRedirectUriEnforceExecutor(ClientProfileRepresentation profileRep) { protected void assertExpectedSecureRedirectUriEnforceExecutor(ClientProfileRepresentation profileRep) {
assertExpectedNoConfigElement(SecureClientRegisteringUriEnforceExecutorFactory.PROVIDER_ID, profileRep, (ClientProfileRepresentation i)->i.getExecutors()); assertExpectedEmptyConfig(SecureClientRegisteringUriEnforceExecutorFactory.PROVIDER_ID, profileRep);
} }
protected void assertExpectedSecureRequestObjectExecutor(ClientProfileRepresentation profileRep) { protected void assertExpectedSecureRequestObjectExecutor(ClientProfileRepresentation profileRep) {
assertExpectedNoConfigElement(SecureRequestObjectExecutorFactory.PROVIDER_ID, profileRep, (ClientProfileRepresentation i)->i.getExecutors()); assertExpectedEmptyConfig(SecureRequestObjectExecutorFactory.PROVIDER_ID, profileRep);
} }
protected void assertExpectedSecureResponseTypeExecutor(ClientProfileRepresentation profileRep) { protected void assertExpectedSecureResponseTypeExecutor(ClientProfileRepresentation profileRep) {
assertExpectedNoConfigElement(SecureResponseTypeExecutorFactory.PROVIDER_ID, profileRep, (ClientProfileRepresentation i)->i.getExecutors()); assertExpectedEmptyConfig(SecureResponseTypeExecutorFactory.PROVIDER_ID, profileRep);
} }
protected void assertExpectedSecureSessionEnforceExecutor(ClientProfileRepresentation profileRep) { protected void assertExpectedSecureSessionEnforceExecutor(ClientProfileRepresentation profileRep) {
assertExpectedNoConfigElement(SecureSessionEnforceExecutorFactory.PROVIDER_ID, profileRep, (ClientProfileRepresentation i)->i.getExecutors()); assertExpectedEmptyConfig(SecureSessionEnforceExecutorFactory.PROVIDER_ID, profileRep);
} }
protected void assertExpectedSecureSigningAlgorithmEnforceExecutor(ClientProfileRepresentation profileRep) { protected void assertExpectedSecureSigningAlgorithmEnforceExecutor(ClientProfileRepresentation profileRep) {
assertExpectedNoConfigElement(SecureSigningAlgorithmEnforceExecutorFactory.PROVIDER_ID, profileRep, (ClientProfileRepresentation i)->i.getExecutors()); assertExpectedEmptyConfig(SecureSigningAlgorithmEnforceExecutorFactory.PROVIDER_ID, profileRep);
} }
protected void assertExpectedSecureSigningAlgorithmForSignedJwtEnforceExecutor(ClientProfileRepresentation profileRep) { protected void assertExpectedSecureSigningAlgorithmForSignedJwtEnforceExecutor(ClientProfileRepresentation profileRep) {
assertExpectedNoConfigElement(SecureSigningAlgorithmForSignedJwtEnforceExecutorFactory.PROVIDER_ID, profileRep, (ClientProfileRepresentation i)->i.getExecutors()); assertExpectedEmptyConfig(SecureSigningAlgorithmForSignedJwtEnforceExecutorFactory.PROVIDER_ID, profileRep);
} }
protected JsonNode assertExpectedAugmenedExecutor(boolean isAugment, String providerId, ClientProfileRepresentation profileRep) { protected void assertExpectedAugmenedExecutor(boolean isAugment, String providerId, ClientProfileRepresentation profileRep) {
assertNotNull(profileRep); assertNotNull(profileRep);
JsonNode actualExecutorConfig = getConfig(profileRep.getExecutors(), providerId); Map<String, Object> actualExecutorConfig = getConfigOfExecutor(providerId, profileRep);
assertNotNull(actualExecutorConfig); assertNotNull(actualExecutorConfig);
boolean actualIsAugment = actualExecutorConfig.get("is-augment") == null ? false : (Boolean) actualExecutorConfig.get("is-augment");
boolean actualIsAugment = false;
if (actualExecutorConfig.findValue("is-augment") != null) actualIsAugment = actualExecutorConfig.findValue("is-augment").asBoolean();
assertEquals(isAugment, actualIsAugment); assertEquals(isAugment, actualIsAugment);
}
return actualExecutorConfig; private Map<String, Object> getConfigOfExecutor(String providerId, ClientProfileRepresentation profileRep) {
ClientPolicyExecutorRepresentation executorRep = profileRep.getExecutors().stream()
.filter(profileRepp -> providerId.equals(profileRepp.getExecutorProviderId()))
.findFirst().orElse(null);
return executorRep == null ? null : executorRep.getConfiguration().getConfigAsMap();
} }
// Assertions about policies // Assertions about policies
@ -1429,94 +1355,69 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
assertEquals(new HashSet<>(expectedPolicies), actualPolicies); assertEquals(new HashSet<>(expectedPolicies), actualPolicies);
} }
protected void assertExpectedPolicy(String name, String description, boolean isBuiltin, boolean isEnabled, List<String> profiles, ClientPolicyRepresentation actualPolicyRep) { protected void assertExpectedPolicy(String name, String description, boolean isEnabled, List<String> profiles, ClientPolicyRepresentation actualPolicyRep) {
assertNotNull(actualPolicyRep); assertNotNull(actualPolicyRep);
assertEquals(description, actualPolicyRep.getDescription()); assertEquals(description, actualPolicyRep.getDescription());
assertEquals(isBuiltin, actualPolicyRep.isBuiltin()); assertEquals(isEnabled, actualPolicyRep.isEnabled());
assertEquals(isEnabled, actualPolicyRep.isEnable());
assertEquals(new HashSet<>(profiles), new HashSet<>(actualPolicyRep.getProfiles())); assertEquals(new HashSet<>(profiles), new HashSet<>(actualPolicyRep.getProfiles()));
} }
// conditions // conditions
protected void assertExpectedConditions(List<String> expectedConditions, ClientPolicyRepresentation policyRep) { protected void assertExpectedConditions(List<String> expectedConditions, ClientPolicyRepresentation policyRep) {
assertExpetedElement(expectedConditions, policyRep, (ClientPolicyRepresentation i)->i.getConditions()); List<String> actualConditionNames = policyRep.getConditions().stream()
.map(ClientPolicyConditionRepresentation::getConditionProviderId)
.collect(Collectors.toList());
assertThat(actualConditionNames, Matchers.containsInAnyOrder(expectedConditions.toArray()));
} }
protected void assertExpectedAnyClientCondition(ClientPolicyRepresentation profileRep) { protected void assertExpectedAnyClientCondition(ClientPolicyRepresentation policyRep) {
assertExpectedNoConfigElement(AnyClientConditionFactory.PROVIDER_ID, profileRep, (ClientPolicyRepresentation i)->i.getConditions()); ClientPolicyConditionConfigurationRepresentation config = getConfigAsExpectedType(policyRep, AnyClientConditionFactory.PROVIDER_ID, ClientPolicyConditionConfigurationRepresentation.class);
Assert.assertTrue("Expected empty configuration for provider " + AnyClientConditionFactory.PROVIDER_ID, config.getConfigAsMap().isEmpty());
} }
protected void assertExpectedClientAccessTypeCondition(List<String> type, ClientPolicyRepresentation policyRep) { protected void assertExpectedClientAccessTypeCondition(List<String> type, ClientPolicyRepresentation policyRep) {
JsonNode actualConditionConfig = getConfig(policyRep.getConditions(), ClientAccessTypeConditionFactory.PROVIDER_ID); ClientAccessTypeCondition.Configuration cfg = getConfigAsExpectedType(policyRep, ClientAccessTypeConditionFactory.PROVIDER_ID, ClientAccessTypeCondition.Configuration.class);
Assert.assertEquals(cfg.getType(), type);
Set<String> actualTypes = new HashSet<>();
if (actualConditionConfig.findValue("type") != null)
actualConditionConfig.findValue("type").elements().forEachRemaining(i->actualTypes.add(i.asText()));
assertEquals(new HashSet<>(type), actualTypes);
} }
protected void assertExpectedClientRolesCondition(List<String> roles, ClientPolicyRepresentation policyRep) { protected void assertExpectedClientRolesCondition(List<String> roles, ClientPolicyRepresentation policyRep) {
JsonNode actualConditionConfig = getConfig(policyRep.getConditions(), ClientRolesConditionFactory.PROVIDER_ID); ClientRolesCondition.Configuration cfg = getConfigAsExpectedType(policyRep, ClientRolesConditionFactory.PROVIDER_ID, ClientRolesCondition.Configuration.class);
Assert.assertEquals(cfg.getRoles(), roles);
Set<String> actualRoles = new HashSet<>();
if (actualConditionConfig.findValue("roles") != null)
actualConditionConfig.findValue("roles").elements().forEachRemaining(i->actualRoles.add(i.asText()));
assertEquals(new HashSet<>(roles), actualRoles);
} }
protected void assertExpectedClientScopesCondition(String type, List<String> scopes, ClientPolicyRepresentation policyRep) { protected void assertExpectedClientScopesCondition(String type, List<String> scopes, ClientPolicyRepresentation policyRep) {
JsonNode actualConditionConfig = getConfig(policyRep.getConditions(), ClientScopesConditionFactory.PROVIDER_ID); ClientScopesCondition.Configuration cfg = getConfigAsExpectedType(policyRep, ClientScopesConditionFactory.PROVIDER_ID, ClientScopesCondition.Configuration.class);
Assert.assertEquals(cfg.getType(), type);
String actualType = null; Assert.assertEquals(cfg.getScope(), scopes);
if (actualConditionConfig.findValue("type") != null) actualType = actualConditionConfig.findValue("type").asText();
assertEquals(type, actualType);
Set<String> actualScopes = new HashSet<>();
if (actualConditionConfig.findValue("scope") != null)
actualConditionConfig.findValue("scope").elements().forEachRemaining(i->actualScopes.add(i.asText()));
assertEquals(new HashSet<>(scopes), actualScopes);
} }
protected void assertExpectedClientUpdateContextCondition(List<String> updateClientSources, ClientPolicyRepresentation policyRep) { protected void assertExpectedClientUpdateContextCondition(List<String> updateClientSources, ClientPolicyRepresentation policyRep) {
JsonNode actualConditionConfig = getConfig(policyRep.getConditions(), ClientUpdateContextConditionFactory.PROVIDER_ID); ClientUpdateContextCondition.Configuration cfg = getConfigAsExpectedType(policyRep, ClientUpdateContextConditionFactory.PROVIDER_ID, ClientUpdateContextCondition.Configuration.class);
Assert.assertEquals(cfg.getUpdateClientSource(), updateClientSources);
Set<String> actualUpdateClientSources = new HashSet<>();
if (actualConditionConfig.findValue("update-client-source") != null)
actualConditionConfig.findValue("update-client-source").elements().forEachRemaining(i->actualUpdateClientSources.add(i.asText()));
assertEquals(new HashSet<>(updateClientSources), actualUpdateClientSources);
} }
protected void assertExpectedClientUpdateSourceGroupsCondition(List<String> groups, ClientPolicyRepresentation policyRep) { protected void assertExpectedClientUpdateSourceGroupsCondition(List<String> groups, ClientPolicyRepresentation policyRep) {
JsonNode actualConditionConfig = getConfig(policyRep.getConditions(), ClientUpdateSourceGroupsConditionFactory.PROVIDER_ID); ClientUpdateSourceGroupsCondition.Configuration cfg = getConfigAsExpectedType(policyRep, ClientUpdateSourceGroupsConditionFactory.PROVIDER_ID, ClientUpdateSourceGroupsCondition.Configuration.class);
Assert.assertEquals(cfg.getGroups(), groups);
Set<String> actualGroups = new HashSet<>();
if (actualConditionConfig.findValue("groups") != null)
actualConditionConfig.findValue("groups").elements().forEachRemaining(i->actualGroups.add(i.asText()));
assertEquals(new HashSet<>(groups), actualGroups);
} }
protected void assertExpectedClientUpdateSourceHostsCondition(List<String> trustedHosts, List<Boolean> hostSendingRequestMustMatch, ClientPolicyRepresentation policyRep) { protected void assertExpectedClientUpdateSourceHostsCondition(List<String> trustedHosts, ClientPolicyRepresentation policyRep) {
JsonNode actualConditionConfig = getConfig(policyRep.getConditions(), ClientUpdateSourceHostsConditionFactory.PROVIDER_ID); ClientUpdateSourceHostsCondition.Configuration cfg = getConfigAsExpectedType(policyRep, ClientUpdateSourceHostsConditionFactory.PROVIDER_ID, ClientUpdateSourceHostsCondition.Configuration.class);
Assert.assertEquals(cfg.getTrustedHosts(), trustedHosts);
List<String> actualTrustedHosts = new ArrayList<>();
if (actualConditionConfig.findValue("trusted-hosts") != null)
actualConditionConfig.findValue("trusted-hosts").elements().forEachRemaining(i->actualTrustedHosts.add(i.asText()));
assertEquals(trustedHosts, actualTrustedHosts);
List<Boolean> actualHostSendingRequestMustMatch = new ArrayList<>();
if (actualConditionConfig.findValue("host-sending-request-must-match") != null)
actualConditionConfig.findValue("host-sending-request-must-match").elements().forEachRemaining(i->actualHostSendingRequestMustMatch.add(i.asBoolean()));
assertEquals(trustedHosts, actualTrustedHosts);
} }
protected void assertExpectedClientUpdateSourceRolesCondition(List<String> roles, ClientPolicyRepresentation policyRep) { protected void assertExpectedClientUpdateSourceRolesCondition(List<String> roles, ClientPolicyRepresentation policyRep) {
JsonNode actualConditionConfig = getConfig(policyRep.getConditions(), ClientUpdateSourceRolesConditionFactory.PROVIDER_ID); ClientUpdateSourceRolesCondition.Configuration cfg = getConfigAsExpectedType(policyRep, ClientUpdateSourceRolesConditionFactory.PROVIDER_ID, ClientUpdateSourceRolesCondition.Configuration.class);
Assert.assertEquals(cfg.getRoles(), roles);
}
Set<String> actualRoles = new HashSet<>(); private <CFG extends ClientPolicyConditionConfigurationRepresentation> CFG getConfigAsExpectedType(ClientPolicyRepresentation policyRep, String conditionProviderId, Class<CFG> configClass) {
if (actualConditionConfig.findValue("roles") != null) ClientPolicyConditionRepresentation conditionRep = policyRep.getConditions().stream()
actualConditionConfig.findValue("roles").elements().forEachRemaining(i->actualRoles.add(i.asText())); .filter(condition -> conditionProviderId.equals(condition.getConditionProviderId()))
assertEquals(new HashSet<>(roles), actualRoles); .findFirst().orElseThrow(() -> new AssertionError("Expected to contain configuration for condition " + conditionProviderId));
return JsonSerialization.mapper.convertValue(conditionRep.getConfiguration(), configClass);
} }
// profiles/policies common (compounds) // profiles/policies common (compounds)
@ -1531,7 +1432,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
return rep; return rep;
} }
private <T, R> void assertExpetedCompounds(List<String> expected, R rep, Function<R, List<T>> f, Function<T, String> g) { private <T, R> void assertExpectedCompounds(List<String> expected, R rep, Function<R, List<T>> f, Function<T, String> g) {
assertNotNull(rep); assertNotNull(rep);
List<T> reps = f.apply(rep); List<T> reps = f.apply(rep);
if (reps == null) { if (reps == null) {
@ -1553,33 +1454,9 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
return reps.get(0); return reps.get(0);
} }
// condition/executor common (element) private void assertExpectedEmptyConfig(String executorProviderId, ClientProfileRepresentation profileRep) {
Map<String, Object> config = getConfigOfExecutor(executorProviderId, profileRep);
private <T> void assertExpetedElement(List<String> expected, T rep, Function<T, List<Object>> f) { Assert.assertTrue("Expected empty configuration for provider " + executorProviderId, config.isEmpty());
assertNotNull(rep);
List<Object> objs = f.apply(rep);
if (objs == null) {
assertNull(expected);
return;
}
Set<String> actual = objs.stream().map(i->{
JsonNode node = objectMapper.convertValue(i, JsonNode.class);
return node.fieldNames().next();
}).collect(Collectors.toSet());
assertEquals(new HashSet<>(expected), actual);
}
private <T> void assertExpectedNoConfigElement(String providerId, T rep, Function<T, List<Object>> f) {
assertNotNull(rep);
JsonNode actualConfig = getConfig(f.apply(rep), providerId);
assertEquals("", actualConfig.asText());
}
private JsonNode getConfig(List<Object> objs, String providerId) {
List<JsonNode> nodes = objs.stream().map(i->objectMapper.convertValue(i, JsonNode.class))
.filter(j->j.fieldNames().next().equals(providerId)).collect(Collectors.toList());
if (nodes == null || nodes.size() != 1) return null;
return nodes.get(0);
} }
} }

View file

@ -67,7 +67,7 @@ public class ClientPoliciesImportExportTest extends AbstractClientPoliciesTest {
String targetFilePath = testingClient.testing().exportImport().getExportImportTestDirectory() + File.separator + "client-policies-exported-realm.json"; String targetFilePath = testingClient.testing().exportImport().getExportImportTestDirectory() + File.separator + "client-policies-exported-realm.json";
testingClient.testing().exportImport().setFile(targetFilePath); testingClient.testing().exportImport().setFile(targetFilePath);
loadValidProfilesAndPolicies(); setupValidProfilesAndPolicies();
testRealmExportImport(); testRealmExportImport();
} }
@ -92,13 +92,13 @@ public class ClientPoliciesImportExportTest extends AbstractClientPoliciesTest {
Assert.assertNames(adminClient.realms().findAll(), "master", "test"); Assert.assertNames(adminClient.realms().findAll(), "master", "test");
assertExpectedLoadedProfiles((ClientProfilesRepresentation reps)->{ assertExpectedLoadedProfiles((ClientProfilesRepresentation reps)->{
ClientProfileRepresentation rep = getProfileRepresentation(reps, "ordinal-test-profile"); ClientProfileRepresentation rep = getProfileRepresentation(reps, "ordinal-test-profile", false);
assertExpectedProfile(rep, "ordinal-test-profile", "The profile that can be loaded.", false); assertExpectedProfile(rep, "ordinal-test-profile", "The profile that can be loaded.");
}); });
assertExpectedLoadedPolicies((ClientPoliciesRepresentation reps)->{ assertExpectedLoadedPolicies((ClientPoliciesRepresentation reps)->{
ClientPolicyRepresentation rep = getPolicyRepresentation(reps, "new-policy"); ClientPolicyRepresentation rep = getPolicyRepresentation(reps, "new-policy");
assertExpectedPolicy("new-policy", "duplicated profiles are ignored.", false, true, Arrays.asList("builtin-default-profile", "ordinal-test-profile", "lack-of-builtin-field-test-profile"), assertExpectedPolicy("new-policy", "duplicated profiles are ignored.", true, Arrays.asList("global-default-profile", "ordinal-test-profile", "lack-of-builtin-field-test-profile"),
rep); rep);
}); });
} }

View file

@ -17,17 +17,23 @@
package org.keycloak.testsuite.client; package org.keycloak.testsuite.client;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson; import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.REMOTE; import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.REMOTE;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.hamcrest.Matchers;
import org.junit.Test; import org.junit.Test;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator; import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator;
import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator; import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
import org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator;
import org.keycloak.authentication.authenticators.client.X509ClientAuthenticator;
import org.keycloak.common.Profile; import org.keycloak.common.Profile;
import org.keycloak.representations.idm.ClientPoliciesRepresentation; import org.keycloak.representations.idm.ClientPoliciesRepresentation;
import org.keycloak.representations.idm.ClientPolicyRepresentation; import org.keycloak.representations.idm.ClientPolicyRepresentation;
@ -36,12 +42,12 @@ import org.keycloak.representations.idm.ClientProfilesRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPoliciesUtil; import org.keycloak.services.clientpolicy.ClientPoliciesUtil;
import org.keycloak.services.clientpolicy.condition.AnyClientConditionFactory;
import org.keycloak.services.clientpolicy.condition.ClientAccessTypeConditionFactory; import org.keycloak.services.clientpolicy.condition.ClientAccessTypeConditionFactory;
import org.keycloak.services.clientpolicy.condition.ClientRolesConditionFactory; import org.keycloak.services.clientpolicy.condition.ClientRolesConditionFactory;
import org.keycloak.services.clientpolicy.executor.PKCEEnforceExecutorFactory; import org.keycloak.services.clientpolicy.executor.PKCEEnforceExecutorFactory;
import org.keycloak.services.clientpolicy.executor.SecureClientAuthEnforceExecutorFactory; import org.keycloak.services.clientpolicy.executor.SecureClientAuthEnforceExecutorFactory;
import org.keycloak.services.clientpolicy.executor.SecureSessionEnforceExecutorFactory; import org.keycloak.services.clientpolicy.executor.SecureSessionEnforceExecutorFactory;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude; import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
@ -67,53 +73,51 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
@Test @Test
public void testLoadBuiltinProfilesAndPolicies() throws Exception { public void testLoadBuiltinProfilesAndPolicies() throws Exception {
// retrieve loaded builtin profiles // retrieve loaded global profiles
ClientProfilesRepresentation actualProfilesRep = getProfiles(); ClientProfilesRepresentation actualProfilesRep = getProfilesWithGlobals();
// same profiles // same profiles
assertExpectedProfiles(actualProfilesRep, Arrays.asList("builtin-default-profile")); assertExpectedProfiles(actualProfilesRep, Arrays.asList("global-default-profile"), Collections.emptyList());
// each profile // each profile
ClientProfileRepresentation actualProfileRep = getProfileRepresentation(actualProfilesRep, "builtin-default-profile"); ClientProfileRepresentation actualProfileRep = getProfileRepresentation(actualProfilesRep, "global-default-profile", true);
assertExpectedProfile(actualProfileRep, "builtin-default-profile", "The built-in default profile for enforcing basic security level to clients.", true); assertExpectedProfile(actualProfileRep, "global-default-profile", "The global default profile for enforcing basic security level to clients.");
// each executor // each executor
assertExpectedExecutors(Arrays.asList(SecureSessionEnforceExecutorFactory.PROVIDER_ID), actualProfileRep); assertExpectedExecutors(Arrays.asList(SecureSessionEnforceExecutorFactory.PROVIDER_ID), actualProfileRep);
// Check the "get" request without globals. Assert nothing loaded
actualProfilesRep = getProfilesWithoutGlobals();
assertExpectedProfiles(actualProfilesRep, null, Collections.emptyList());
// retrieve loaded builtin policies // retrieve loaded builtin policies
ClientPoliciesRepresentation actualPoliciesRep = getPolicies(); ClientPoliciesRepresentation actualPoliciesRep = getPolicies();
// same policies // No global policies expected
assertExpectedPolicies(Arrays.asList("builtin-default-policy"), actualPoliciesRep); assertExpectedPolicies(Collections.emptyList(), actualPoliciesRep);
// each policy
ClientPolicyRepresentation actualPolicyRep = getPolicyRepresentation(actualPoliciesRep, "builtin-default-policy"); ClientPolicyRepresentation actualPolicyRep = getPolicyRepresentation(actualPoliciesRep, "builtin-default-policy");
assertExpectedPolicy("builtin-default-policy", "The built-in default policy applied to all clients.", true, false, Arrays.asList("builtin-default-profile"), actualPolicyRep); Assert.assertNull(actualPolicyRep);
// each condition
assertExpectedConditions(Arrays.asList(AnyClientConditionFactory.PROVIDER_ID), actualPolicyRep);
} }
@Test @Test
public void testUpdateValidProfilesAndPolicies() throws Exception { public void testUpdateValidProfilesAndPolicies() throws Exception {
loadValidProfilesAndPolicies(); setupValidProfilesAndPolicies();
assertExpectedLoadedProfiles((ClientProfilesRepresentation reps)->{ assertExpectedLoadedProfiles((ClientProfilesRepresentation reps)->{
ClientProfileRepresentation rep = getProfileRepresentation(reps, "ordinal-test-profile"); ClientProfileRepresentation rep = getProfileRepresentation(reps, "ordinal-test-profile", false);
assertExpectedProfile(rep, "ordinal-test-profile", "The profile that can be loaded.", false); assertExpectedProfile(rep, "ordinal-test-profile", "The profile that can be loaded.");
}); });
assertExpectedLoadedPolicies((ClientPoliciesRepresentation reps)->{ assertExpectedLoadedPolicies((ClientPoliciesRepresentation reps)->{
ClientPolicyRepresentation rep = getPolicyRepresentation(reps, "new-policy"); ClientPolicyRepresentation rep = getPolicyRepresentation(reps, "new-policy");
assertExpectedPolicy("new-policy", "duplicated profiles are ignored.", false, true, Arrays.asList("builtin-default-profile", "ordinal-test-profile", "lack-of-builtin-field-test-profile"), assertExpectedPolicy("new-policy", "duplicated profiles are ignored.", true, Arrays.asList("global-default-profile", "ordinal-test-profile", "lack-of-builtin-field-test-profile"),
rep); rep);
}); });
// update existing profiles // update existing profiles
String modifiedProfileDescription = "The profile has been updated."; String modifiedProfileDescription = "The profile has been updated.";
ClientProfilesRepresentation actualProfilesRep = getProfilesWithoutBuiltin(); ClientProfilesRepresentation actualProfilesRep = getProfilesWithoutGlobals();
ClientProfilesBuilder profilesBuilder = new ClientProfilesBuilder(); ClientProfilesBuilder profilesBuilder = new ClientProfilesBuilder();
actualProfilesRep.getProfiles().stream().forEach(i->{ actualProfilesRep.getProfiles().stream().forEach(i->{
if (i.getName().equals("ordinal-test-profile")) { if (i.getName().equals("ordinal-test-profile")) {
@ -124,19 +128,19 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
updateProfiles(profilesBuilder.toString()); updateProfiles(profilesBuilder.toString());
assertExpectedLoadedProfiles((ClientProfilesRepresentation reps)->{ assertExpectedLoadedProfiles((ClientProfilesRepresentation reps)->{
ClientProfileRepresentation rep = getProfileRepresentation(reps, "ordinal-test-profile"); ClientProfileRepresentation rep = getProfileRepresentation(reps, "ordinal-test-profile", false);
assertExpectedProfile(rep, "ordinal-test-profile", modifiedProfileDescription, false); assertExpectedProfile(rep, "ordinal-test-profile", modifiedProfileDescription);
}); });
// update existing policies // update existing policies
String modifiedPolicyDescription = "The policy has also been updated."; String modifiedPolicyDescription = "The policy has also been updated.";
ClientPoliciesRepresentation actualPoliciesRep = getPoliciesWithoutBuiltin(); ClientPoliciesRepresentation actualPoliciesRep = getPolicies();
ClientPoliciesBuilder policiesBuilder = new ClientPoliciesBuilder(); ClientPoliciesBuilder policiesBuilder = new ClientPoliciesBuilder();
actualPoliciesRep.getPolicies().stream().forEach(i->{ actualPoliciesRep.getPolicies().stream().forEach(i->{
if (i.getName().equals("new-policy")) { if (i.getName().equals("new-policy")) {
i.setDescription(modifiedPolicyDescription); i.setDescription(modifiedPolicyDescription);
i.setEnable(null); i.setEnabled(null);
} }
policiesBuilder.addPolicy(i); policiesBuilder.addPolicy(i);
}); });
@ -144,7 +148,7 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
assertExpectedLoadedPolicies((ClientPoliciesRepresentation reps)->{ assertExpectedLoadedPolicies((ClientPoliciesRepresentation reps)->{
ClientPolicyRepresentation rep = getPolicyRepresentation(reps, "new-policy"); ClientPolicyRepresentation rep = getPolicyRepresentation(reps, "new-policy");
assertExpectedPolicy("new-policy", modifiedPolicyDescription, false, false, Arrays.asList("builtin-default-profile", "ordinal-test-profile", "lack-of-builtin-field-test-profile"), assertExpectedPolicy("new-policy", modifiedPolicyDescription, false, Arrays.asList("global-default-profile", "ordinal-test-profile", "lack-of-builtin-field-test-profile"),
rep); rep);
}); });
@ -152,10 +156,10 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
@Test @Test
public void testDuplicatedProfiles() throws Exception { public void testDuplicatedProfiles() throws Exception {
String beforeUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfiles()); String beforeUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfilesWithGlobals());
// load profiles // load profiles
ClientProfileRepresentation duplicatedProfileRep = (new ClientProfileBuilder()).createProfile("builtin-basic-security", "Enforce basic security level", Boolean.TRUE, null) ClientProfileRepresentation duplicatedProfileRep = (new ClientProfileBuilder()).createProfile("builtin-basic-security", "Enforce basic security level")
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID,
createSecureClientAuthEnforceExecutorConfig( createSecureClientAuthEnforceExecutorConfig(
Boolean.FALSE, Boolean.FALSE,
@ -167,7 +171,7 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
createPKCEEnforceExecutorConfig(Boolean.TRUE)) createPKCEEnforceExecutorConfig(Boolean.TRUE))
.toRepresentation(); .toRepresentation();
ClientProfileRepresentation loadedProfileRep = (new ClientProfileBuilder()).createProfile("ordinal-test-profile", "The profile that can be loaded.", Boolean.FALSE, null) ClientProfileRepresentation loadedProfileRep = (new ClientProfileBuilder()).createProfile("ordinal-test-profile", "The profile that can be loaded.")
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID,
createSecureClientAuthEnforceExecutorConfig( createSecureClientAuthEnforceExecutorConfig(
Boolean.TRUE, Boolean.TRUE,
@ -182,25 +186,43 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
.toString(); .toString();
try { try {
updateProfiles(json); updateProfiles(json);
fail();
} catch (ClientPolicyException cpe) { } catch (ClientPolicyException cpe) {
assertEquals("Bad Request", cpe.getErrorDetail()); assertEquals("Bad Request", cpe.getErrorDetail());
String afterFailedUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfiles()); String afterFailedUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfilesWithGlobals());
assertEquals(beforeUpdateProfilesJson, afterFailedUpdateProfilesJson); assertEquals(beforeUpdateProfilesJson, afterFailedUpdateProfilesJson);
return;
} }
}
@Test
public void testOverwriteBuiltinProfileNotAllowed() throws Exception {
// register profiles
String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile("global-default-profile", "Pershyy Profil")
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID,
createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE,
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID),
X509ClientAuthenticator.PROVIDER_ID))
.toRepresentation()
).toString();
try {
updateProfiles(json);
fail(); fail();
} catch (ClientPolicyException cpe) {
assertEquals("update profiles failed", cpe.getError());
}
} }
@Test @Test
public void testNullProfiles() throws Exception { public void testNullProfiles() throws Exception {
String beforeUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfiles()); String beforeUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfilesWithGlobals());
String json = null; String json = null;
try { try {
updateProfiles(json); updateProfiles(json);
} catch (ClientPolicyException cpe) { } catch (ClientPolicyException cpe) {
assertEquals("Bad Request", cpe.getErrorDetail()); assertEquals("argument \"content\" is null", cpe.getErrorDetail());
String afterFailedUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfiles()); String afterFailedUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfilesWithGlobals());
assertEquals(beforeUpdateProfilesJson, afterFailedUpdateProfilesJson); assertEquals(beforeUpdateProfilesJson, afterFailedUpdateProfilesJson);
return; return;
} }
@ -209,7 +231,7 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
@Test @Test
public void testInvalidFormattedJsonProfiles() throws Exception { public void testInvalidFormattedJsonProfiles() throws Exception {
String beforeUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfiles()); String beforeUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfilesWithGlobals());
String json = "{\n" String json = "{\n"
+ " \"profiles\": [\n" + " \"profiles\": [\n"
@ -232,8 +254,8 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
try { try {
updateProfiles(json); updateProfiles(json);
} catch (ClientPolicyException cpe) { } catch (ClientPolicyException cpe) {
assertEquals("Bad Request", cpe.getErrorDetail()); assertThat(cpe.getErrorDetail(), Matchers.startsWith("Unrecognized field"));
String afterFailedUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfiles()); String afterFailedUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfilesWithGlobals());
assertEquals(beforeUpdateProfilesJson, afterFailedUpdateProfilesJson); assertEquals(beforeUpdateProfilesJson, afterFailedUpdateProfilesJson);
return; return;
} }
@ -242,7 +264,7 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
@Test @Test
public void testInvalidFieldTypeJsonProfiles() throws Exception { public void testInvalidFieldTypeJsonProfiles() throws Exception {
String beforeUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfiles()); String beforeUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfilesWithGlobals());
String json = "{\n" String json = "{\n"
+ " \"profiles\": [\n" + " \"profiles\": [\n"
@ -250,14 +272,12 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
+ " \"name\" : \"ordinal-test-profile\",\n" + " \"name\" : \"ordinal-test-profile\",\n"
+ " \"description\" : \"Not builtin profile that should be skipped.\",\n" + " \"description\" : \"Not builtin profile that should be skipped.\",\n"
+ " \"builtin\" : \"no\",\n" + " \"builtin\" : \"no\",\n"
+ " \"executors\": [\n" + " \"executors\": {\n"
+ " {\n"
+ " \"new-secure-client-authn-executor\": {\n" + " \"new-secure-client-authn-executor\": {\n"
+ " \"client-authns\": [ \"private-key-jwt\" ],\n" + " \"client-authns\": [ \"private-key-jwt\" ],\n"
+ " \"client-authns-augment\" : \"private-key-jwt\",\n" + " \"client-authns-augment\" : \"private-key-jwt\",\n"
+ " \"is-augment\" : true\n" + " \"is-augment\" : true\n"
+ " }\n" + " }\n"
+ " }\n"
+ " ]\n" + " ]\n"
+ " }\n" + " }\n"
+ " ]\n" + " ]\n"
@ -265,8 +285,8 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
try { try {
updateProfiles(json); updateProfiles(json);
} catch (ClientPolicyException cpe) { } catch (ClientPolicyException cpe) {
assertEquals("Bad Request", cpe.getErrorDetail()); assertThat(cpe.getErrorDetail(), Matchers.startsWith("Unrecognized field "));
String afterFailedUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfiles()); String afterFailedUpdateProfilesJson = ClientPoliciesUtil.convertClientProfilesRepresentationToJson(getProfilesWithGlobals());
assertEquals(beforeUpdateProfilesJson, afterFailedUpdateProfilesJson); assertEquals(beforeUpdateProfilesJson, afterFailedUpdateProfilesJson);
return; return;
} }
@ -282,24 +302,21 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
(new ClientPolicyBuilder()).createPolicy( (new ClientPolicyBuilder()).createPolicy(
"builtin-duplicated-new-policy", "builtin-duplicated-new-policy",
"builtin duplicated new policy is ignored.", "builtin duplicated new policy is ignored.",
Boolean.FALSE, Boolean.TRUE)
Boolean.TRUE,
null,
Arrays.asList("builtin-default-profile"))
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE))) createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE)))
.addProfile("global-default-profile")
.toRepresentation(); .toRepresentation();
ClientPolicyRepresentation loadedPolicyRep = ClientPolicyRepresentation loadedPolicyRep =
(new ClientPolicyBuilder()).createPolicy( (new ClientPolicyBuilder()).createPolicy(
"new-policy", "new-policy",
"duplicated profiles are ignored.", "duplicated profiles are ignored.",
Boolean.FALSE, Boolean.TRUE)
Boolean.TRUE,
null,
Arrays.asList("lack-of-builtin-field-test-profile", "ordinal-test-profile"))
.addCondition(ClientAccessTypeConditionFactory.PROVIDER_ID, .addCondition(ClientAccessTypeConditionFactory.PROVIDER_ID,
createClientAccessTypeConditionConfig(Arrays.asList(ClientAccessTypeConditionFactory.TYPE_PUBLIC, ClientAccessTypeConditionFactory.TYPE_BEARERONLY))) createClientAccessTypeConditionConfig(Arrays.asList(ClientAccessTypeConditionFactory.TYPE_PUBLIC, ClientAccessTypeConditionFactory.TYPE_BEARERONLY)))
.addProfile("lack-of-builtin-field-test-profile")
.addProfile("ordinal-test-profile")
.toRepresentation(); .toRepresentation();
String json = (new ClientPoliciesBuilder()) String json = (new ClientPoliciesBuilder())
@ -359,7 +376,7 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
try { try {
updatePolicies(json); updatePolicies(json);
} catch (ClientPolicyException cpe) { } catch (ClientPolicyException cpe) {
assertEquals("Bad Request", cpe.getErrorDetail()); assertThat(cpe.getErrorDetail(), Matchers.startsWith("Unrecognized field "));
String afterFailedUpdatePoliciesJson = ClientPoliciesUtil.convertClientPoliciesRepresentationToJson(getPolicies()); String afterFailedUpdatePoliciesJson = ClientPoliciesUtil.convertClientPoliciesRepresentationToJson(getPolicies());
assertEquals(beforeUpdatePoliciesJson, afterFailedUpdatePoliciesJson); assertEquals(beforeUpdatePoliciesJson, afterFailedUpdatePoliciesJson);
return; return;
@ -386,11 +403,31 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
try { try {
updatePolicies(json); updatePolicies(json);
} catch (ClientPolicyException cpe) { } catch (ClientPolicyException cpe) {
assertEquals("Bad Request", cpe.getErrorDetail()); assertThat(cpe.getErrorDetail(), Matchers.startsWith("Unrecognized field "));
String afterFailedUpdatePoliciesJson = ClientPoliciesUtil.convertClientPoliciesRepresentationToJson(getPolicies()); String afterFailedUpdatePoliciesJson = ClientPoliciesUtil.convertClientPoliciesRepresentationToJson(getPolicies());
assertEquals(beforeUpdatePoliciesJson, afterFailedUpdatePoliciesJson); assertEquals(beforeUpdatePoliciesJson, afterFailedUpdatePoliciesJson);
return; return;
} }
fail(); fail();
} }
// Test that regular CRUD of realm representation object through admin REST API does not remove
@Test
public void testCRUDRealmRepresentation() throws Exception {
setupValidProfilesAndPolicies();
// Get the realm and assert that expected policies and profiles are present
RealmResource testRealm = realmsResouce().realm("test");
RealmRepresentation realmRep = testRealm.toRepresentation();
assertExpectedProfiles(realmRep.getClientProfiles(), null, Arrays.asList("ordinal-test-profile", "lack-of-builtin-field-test-profile"));
assertExpectedPolicies(Arrays.asList("new-policy", "lack-of-builtin-field-test-policy"), realmRep.getClientPolicies());
// Update the realm
testRealm.update(realmRep);
// Test the realm again
realmRep = testRealm.toRepresentation();
assertExpectedProfiles(realmRep.getClientProfiles(), null, Arrays.asList("ordinal-test-profile", "lack-of-builtin-field-test-profile"));
assertExpectedPolicies(Arrays.asList("new-policy", "lack-of-builtin-field-test-policy"), realmRep.getClientPolicies());
}
} }

View file

@ -249,7 +249,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testAdminClientAugmentedAuthType() throws Exception { public void testAdminClientAugmentedAuthType() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Pershyy Profil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Pershyy Profil")
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID,
createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE, createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE,
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID), Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID),
@ -260,7 +260,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Persha Polityka", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Persha Polityka", Boolean.TRUE)
.addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID,
createClientUpdateContextConditionConfig(Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER))) createClientUpdateContextConditionConfig(Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER)))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -276,7 +276,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// update profiles // update profiles
json = (new ClientProfilesBuilder()).addProfile( json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Pershyy Profil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Pershyy Profil")
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID,
createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE, createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE,
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID), Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID),
@ -332,7 +332,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testCreateUpdateDeleteConditionRuntime() throws Exception { public void testCreateUpdateDeleteConditionRuntime() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Eichte profil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Eichte profil")
.addExecutor(PKCEEnforceExecutorFactory.PROVIDER_ID, .addExecutor(PKCEEnforceExecutorFactory.PROVIDER_ID,
createPKCEEnforceExecutorConfig(Boolean.TRUE)) createPKCEEnforceExecutorConfig(Boolean.TRUE))
.toRepresentation() .toRepresentation()
@ -350,7 +350,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Dei Eischt Politik", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Dei Eischt Politik", Boolean.TRUE)
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE))) createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE)))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -361,7 +361,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
failLoginByNotFollowingPKCE(clientId); failLoginByNotFollowingPKCE(clientId);
// update policies // update policies
updatePolicy((new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Dei Aktualiseiert Eischt Politik", Boolean.FALSE, Boolean.TRUE, null, null) updatePolicy((new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Dei Aktualiseiert Eischt Politik", Boolean.TRUE)
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList("anothor-client-role"))) createClientRolesConditionConfig(Arrays.asList("anothor-client-role")))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -370,7 +370,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
successfulLoginAndLogout(clientId, clientSecret); successfulLoginAndLogout(clientId, clientSecret);
// update policies // update policies
updatePolicy((new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Dei Aktualiseiert Eischt Politik", Boolean.FALSE, Boolean.TRUE, null, null) updatePolicy((new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Dei Aktualiseiert Eischt Politik", Boolean.TRUE)
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
.toRepresentation()); .toRepresentation());
@ -381,7 +381,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testCreateUpdateDeleteExecutorRuntime() throws Exception { public void testCreateUpdateDeleteExecutorRuntime() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Purofairu Sono Ichi", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Purofairu Sono Ichi")
.addExecutor(PKCEEnforceExecutorFactory.PROVIDER_ID, .addExecutor(PKCEEnforceExecutorFactory.PROVIDER_ID,
createPKCEEnforceExecutorConfig(Boolean.FALSE)) createPKCEEnforceExecutorConfig(Boolean.FALSE))
.toRepresentation() .toRepresentation()
@ -390,7 +390,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Porishii Sono Ichi", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Porishii Sono Ichi", Boolean.TRUE)
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE))) createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE)))
.addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID,
@ -409,7 +409,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
successfulLoginAndLogout(clientId, clientSecret); successfulLoginAndLogout(clientId, clientSecret);
// update policies // update policies
updatePolicy((new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Koushinsareta Porishii Sono Ichi", Boolean.FALSE, Boolean.TRUE, null, null) updatePolicy((new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Koushinsareta Porishii Sono Ichi", Boolean.TRUE)
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE))) createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE)))
.addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID,
@ -421,7 +421,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// update profiles // update profiles
updateProfile( updateProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Koushinsareta Purofairu Sono Ichi", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Koushinsareta Purofairu Sono Ichi")
.addExecutor(PKCEEnforceExecutorFactory.PROVIDER_ID, .addExecutor(PKCEEnforceExecutorFactory.PROVIDER_ID,
createPKCEEnforceExecutorConfig(Boolean.TRUE)) createPKCEEnforceExecutorConfig(Boolean.TRUE))
.toRepresentation()); .toRepresentation());
@ -434,7 +434,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// update profiles // update profiles
updateProfile( updateProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Sarani Koushinsareta Purofairu Sono Ichi", Boolean.FALSE, null).toRepresentation()); (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Sarani Koushinsareta Purofairu Sono Ichi").toRepresentation());
updateClientByAdmin(cid, (ClientRepresentation clientRep) -> { updateClientByAdmin(cid, (ClientRepresentation clientRep) -> {
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setPkceCodeChallengeMethod(null); OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setPkceCodeChallengeMethod(null);
@ -473,11 +473,11 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
String profileAlphaName = "MyProfile-alpha"; String profileAlphaName = "MyProfile-alpha";
String profileBetaName = "MyProfile-beta"; String profileBetaName = "MyProfile-beta";
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(profileAlphaName, "Pierwszy Profil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(profileAlphaName, "Pierwszy Profil")
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID,
createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE, Arrays.asList(ClientIdAndSecretAuthenticator.PROVIDER_ID), ClientIdAndSecretAuthenticator.PROVIDER_ID)) createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE, Arrays.asList(ClientIdAndSecretAuthenticator.PROVIDER_ID), ClientIdAndSecretAuthenticator.PROVIDER_ID))
.toRepresentation()).addProfile( .toRepresentation()).addProfile(
(new ClientProfileBuilder()).createProfile(profileBetaName, "Drugi Profil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(profileBetaName, "Drugi Profil")
.addExecutor(PKCEEnforceExecutorFactory.PROVIDER_ID, .addExecutor(PKCEEnforceExecutorFactory.PROVIDER_ID,
createPKCEEnforceExecutorConfig(Boolean.TRUE)) createPKCEEnforceExecutorConfig(Boolean.TRUE))
.toRepresentation() .toRepresentation()
@ -488,14 +488,14 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
String policyAlphaName = "MyPolicy-alpha"; String policyAlphaName = "MyPolicy-alpha";
String policyBetaName = "MyPolicy-beta"; String policyBetaName = "MyPolicy-beta";
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(policyAlphaName, "Pierwsza Zasada", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(policyAlphaName, "Pierwsza Zasada", Boolean.TRUE)
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList(roleAlphaName, roleZetaName))) createClientRolesConditionConfig(Arrays.asList(roleAlphaName, roleZetaName)))
.addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID,
createClientUpdateContextConditionConfig(Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER))) createClientUpdateContextConditionConfig(Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER)))
.addProfile(profileAlphaName) .addProfile(profileAlphaName)
.toRepresentation()).addPolicy( .toRepresentation()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(policyBetaName, "Drugi Zasada", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(policyBetaName, "Drugi Zasada", Boolean.TRUE)
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList(roleBetaName, roleZetaName))) createClientRolesConditionConfig(Arrays.asList(roleBetaName, roleZetaName)))
.addProfile(profileBetaName) .addProfile(profileBetaName)
@ -530,7 +530,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testIntentionalExceptionOnCondition() throws Exception { public void testIntentionalExceptionOnCondition() throws Exception {
// register policies // register policies
String json = (new ClientPoliciesBuilder()).addPolicy( String json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Fyrsta Stefnan", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Fyrsta Stefnan", Boolean.TRUE)
.addCondition(TestRaiseExeptionConditionFactory.PROVIDER_ID, .addCondition(TestRaiseExeptionConditionFactory.PROVIDER_ID,
createTestRaiseExeptionConditionConfig()) createTestRaiseExeptionConditionConfig())
.toRepresentation() .toRepresentation()
@ -549,7 +549,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testAnyClientCondition() throws Exception { public void testAnyClientCondition() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Le Premier Profil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Le Premier Profil")
.addExecutor(SecureSessionEnforceExecutorFactory.PROVIDER_ID, null) .addExecutor(SecureSessionEnforceExecutorFactory.PROVIDER_ID, null)
.toRepresentation() .toRepresentation()
).toString(); ).toString();
@ -557,7 +557,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Premiere Politique", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Premiere Politique", Boolean.TRUE)
.addCondition(AnyClientConditionFactory.PROVIDER_ID, .addCondition(AnyClientConditionFactory.PROVIDER_ID,
createAnyClientConditionConfig()) createAnyClientConditionConfig())
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -590,7 +590,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testConditionWithoutNoConfiguration() throws Exception { public void testConditionWithoutNoConfiguration() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Die Erste Politik", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Die Erste Politik")
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, null) .addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, null)
.toRepresentation() .toRepresentation()
).toString(); ).toString();
@ -598,22 +598,22 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy("MyPolicy-ClientAccessTypeCondition", "Die Erste Politik", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy("MyPolicy-ClientAccessTypeCondition", "Die Erste Politik", Boolean.TRUE)
.addCondition(ClientAccessTypeConditionFactory.PROVIDER_ID, null) .addCondition(ClientAccessTypeConditionFactory.PROVIDER_ID, null)
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
.toRepresentation() .toRepresentation()
).addPolicy( ).addPolicy(
(new ClientPolicyBuilder()).createPolicy("MyPolicy-ClientUpdateSourceGroupsCondition", "Die Zweite Politik", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy("MyPolicy-ClientUpdateSourceGroupsCondition", "Die Zweite Politik", Boolean.TRUE)
.addCondition(ClientUpdateSourceGroupsConditionFactory.PROVIDER_ID, null) .addCondition(ClientUpdateSourceGroupsConditionFactory.PROVIDER_ID, null)
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
.toRepresentation() .toRepresentation()
).addPolicy( ).addPolicy(
(new ClientPolicyBuilder()).createPolicy("MyPolicy-ClientUpdateSourceRolesCondition", "Die Dritte Politik", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy("MyPolicy-ClientUpdateSourceRolesCondition", "Die Dritte Politik", Boolean.TRUE)
.addCondition(ClientUpdateSourceRolesConditionFactory.PROVIDER_ID, null) .addCondition(ClientUpdateSourceRolesConditionFactory.PROVIDER_ID, null)
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
.toRepresentation() .toRepresentation()
).addPolicy( ).addPolicy(
(new ClientPolicyBuilder()).createPolicy("MyPolicy-ClientUpdateContextCondition", "Die Vierte Politik", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy("MyPolicy-ClientUpdateContextCondition", "Die Vierte Politik", Boolean.TRUE)
.addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID, null) .addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID, null)
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
.toRepresentation() .toRepresentation()
@ -637,7 +637,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testClientUpdateSourceHostsCondition() throws Exception { public void testClientUpdateSourceHostsCondition() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Prvni Profil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Prvni Profil")
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID,
createSecureClientAuthEnforceExecutorConfig( createSecureClientAuthEnforceExecutorConfig(
Boolean.FALSE, Boolean.FALSE,
@ -650,7 +650,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Prvni Politika", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Prvni Politika", Boolean.TRUE)
.addCondition(ClientUpdateSourceHostsConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateSourceHostsConditionFactory.PROVIDER_ID,
createClientUpdateSourceHostsConditionConfig(Arrays.asList("localhost", "127.0.0.1"))) createClientUpdateSourceHostsConditionConfig(Arrays.asList("localhost", "127.0.0.1")))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -671,7 +671,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// update policies // update policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Aktualizovana Prvni Politika", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Aktualizovana Prvni Politika", Boolean.TRUE)
.addCondition(ClientUpdateSourceHostsConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateSourceHostsConditionFactory.PROVIDER_ID,
createClientUpdateSourceHostsConditionConfig(Arrays.asList("example.com"))) createClientUpdateSourceHostsConditionConfig(Arrays.asList("example.com")))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -692,7 +692,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testClientUpdateSourceGroupsCondition() throws Exception { public void testClientUpdateSourceGroupsCondition() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forste Profil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forste Profil")
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID,
createSecureClientAuthEnforceExecutorConfig( createSecureClientAuthEnforceExecutorConfig(
Boolean.FALSE, Boolean.FALSE,
@ -705,7 +705,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Den Forste Politik", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Den Forste Politik", Boolean.TRUE)
.addCondition(ClientUpdateSourceGroupsConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateSourceGroupsConditionFactory.PROVIDER_ID,
createClientUpdateSourceGroupsConditionConfig(Arrays.asList("topGroup"))) createClientUpdateSourceGroupsConditionConfig(Arrays.asList("topGroup")))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -732,7 +732,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testClientUpdateSourceRolesCondition() throws Exception { public void testClientUpdateSourceRolesCondition() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Il Primo Profilo", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Il Primo Profilo")
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID,
createSecureClientAuthEnforceExecutorConfig( createSecureClientAuthEnforceExecutorConfig(
Boolean.FALSE, Boolean.FALSE,
@ -745,9 +745,9 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Prima Politica", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Prima Politica", Boolean.TRUE)
.addCondition(ClientUpdateSourceRolesConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateSourceRolesConditionFactory.PROVIDER_ID,
createClientUpdateSourceRolesConditionConfig(Arrays.asList(AdminRoles.CREATE_CLIENT))) createClientUpdateSourceRolesConditionConfig(Arrays.asList(Constants.REALM_MANAGEMENT_CLIENT_ID + "." + AdminRoles.CREATE_CLIENT)))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
.toRepresentation() .toRepresentation()
).toString(); ).toString();
@ -772,7 +772,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testClientScopesCondition() throws Exception { public void testClientScopesCondition() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Het Eerste Profiel", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Het Eerste Profiel")
.addExecutor(PKCEEnforceExecutorFactory.PROVIDER_ID, .addExecutor(PKCEEnforceExecutorFactory.PROVIDER_ID,
createPKCEEnforceExecutorConfig(Boolean.TRUE)) createPKCEEnforceExecutorConfig(Boolean.TRUE))
.toRepresentation() .toRepresentation()
@ -781,7 +781,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Het Eerste Beleid", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Het Eerste Beleid", Boolean.TRUE)
.addCondition(ClientScopesConditionFactory.PROVIDER_ID, .addCondition(ClientScopesConditionFactory.PROVIDER_ID,
createClientScopesConditionConfig(ClientScopesConditionFactory.OPTIONAL, Arrays.asList("offline_access", "microprofile-jwt"))) createClientScopesConditionConfig(ClientScopesConditionFactory.OPTIONAL, Arrays.asList("offline_access", "microprofile-jwt")))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -815,7 +815,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testClientAccessTypeCondition() throws Exception { public void testClientAccessTypeCondition() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "El Primer Perfil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "El Primer Perfil")
.addExecutor(SecureSessionEnforceExecutorFactory.PROVIDER_ID, null) .addExecutor(SecureSessionEnforceExecutorFactory.PROVIDER_ID, null)
.toRepresentation() .toRepresentation()
).toString(); ).toString();
@ -823,7 +823,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Primera Plitica", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Primera Plitica", Boolean.TRUE)
.addCondition(ClientAccessTypeConditionFactory.PROVIDER_ID, .addCondition(ClientAccessTypeConditionFactory.PROVIDER_ID,
createClientAccessTypeConditionConfig(Arrays.asList(ClientAccessTypeConditionFactory.TYPE_CONFIDENTIAL))) createClientAccessTypeConditionConfig(Arrays.asList(ClientAccessTypeConditionFactory.TYPE_CONFIDENTIAL)))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -854,7 +854,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testSecureResponseTypeExecutor() throws Exception { public void testSecureResponseTypeExecutor() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "O Primeiro Perfil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "O Primeiro Perfil")
.addExecutor(SecureResponseTypeExecutorFactory.PROVIDER_ID, null) .addExecutor(SecureResponseTypeExecutorFactory.PROVIDER_ID, null)
.toRepresentation() .toRepresentation()
).toString(); ).toString();
@ -862,7 +862,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "A Primeira Politica", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "A Primeira Politica", Boolean.TRUE)
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE))) createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE)))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -921,7 +921,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
Integer availablePeriod = Integer.valueOf(SecureRequestObjectExecutor.DEFAULT_AVAILABLE_PERIOD + 400); Integer availablePeriod = Integer.valueOf(SecureRequestObjectExecutor.DEFAULT_AVAILABLE_PERIOD + 400);
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Prvy Profil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Prvy Profil")
.addExecutor(SecureRequestObjectExecutorFactory.PROVIDER_ID, .addExecutor(SecureRequestObjectExecutorFactory.PROVIDER_ID,
createSecureRequestObjectExecutorConfig(availablePeriod, null)) createSecureRequestObjectExecutorConfig(availablePeriod, null))
.toRepresentation() .toRepresentation()
@ -930,7 +930,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Prva Politika", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Prva Politika", Boolean.TRUE)
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE))) createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE)))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -1046,7 +1046,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// update profile : no configuration - "nbf" check and available period is 3600 sec // update profile : no configuration - "nbf" check and available period is 3600 sec
json = (new ClientProfilesBuilder()).addProfile( json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Prvy Profil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Prvy Profil")
.addExecutor(SecureRequestObjectExecutorFactory.PROVIDER_ID, null) .addExecutor(SecureRequestObjectExecutorFactory.PROVIDER_ID, null)
.toRepresentation() .toRepresentation()
).toString(); ).toString();
@ -1078,7 +1078,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// update profile : not check "nbf" // update profile : not check "nbf"
json = (new ClientProfilesBuilder()).addProfile( json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Prvy Profil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Prvy Profil")
.addExecutor(SecureRequestObjectExecutorFactory.PROVIDER_ID, .addExecutor(SecureRequestObjectExecutorFactory.PROVIDER_ID,
createSecureRequestObjectExecutorConfig(null, Boolean.FALSE)) createSecureRequestObjectExecutorConfig(null, Boolean.FALSE))
.toRepresentation() .toRepresentation()
@ -1109,7 +1109,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testSecureSessionEnforceExecutor() throws Exception { public void testSecureSessionEnforceExecutor() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forste Profilen", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forste Profilen")
.addExecutor(SecureSessionEnforceExecutorFactory.PROVIDER_ID, null) .addExecutor(SecureSessionEnforceExecutorFactory.PROVIDER_ID, null)
.toRepresentation() .toRepresentation()
).toString(); ).toString();
@ -1119,7 +1119,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
String roleAlphaName = "sample-client-role-alpha"; String roleAlphaName = "sample-client-role-alpha";
String roleBetaName = "sample-client-role-beta"; String roleBetaName = "sample-client-role-beta";
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Den Forste Politikken", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Den Forste Politikken", Boolean.TRUE)
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList(roleBetaName))) createClientRolesConditionConfig(Arrays.asList(roleBetaName)))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -1164,7 +1164,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testSecureSigningAlgorithmEnforceExecutor() throws Exception { public void testSecureSigningAlgorithmEnforceExecutor() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forsta Profilen", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forsta Profilen")
.addExecutor(SecureSigningAlgorithmEnforceExecutorFactory.PROVIDER_ID, null) .addExecutor(SecureSigningAlgorithmEnforceExecutorFactory.PROVIDER_ID, null)
.toRepresentation() .toRepresentation()
).toString(); ).toString();
@ -1172,7 +1172,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Den Forsta Policyn", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Den Forsta Policyn", Boolean.TRUE)
.addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID,
createClientUpdateContextConditionConfig(Arrays.asList( createClientUpdateContextConditionConfig(Arrays.asList(
ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER, ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER,
@ -1237,7 +1237,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// update profiles, ES256 enforced // update profiles, ES256 enforced
json = (new ClientProfilesBuilder()).addProfile( json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forsta Profilen", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forsta Profilen")
.addExecutor(SecureSigningAlgorithmEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureSigningAlgorithmEnforceExecutorFactory.PROVIDER_ID,
createSecureSigningAlgorithmEnforceExecutorConfig(org.keycloak.crypto.Algorithm.ES256)) createSecureSigningAlgorithmEnforceExecutorConfig(org.keycloak.crypto.Algorithm.ES256))
.toRepresentation() .toRepresentation()
@ -1261,7 +1261,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// update profiles, fall back to PS256 // update profiles, fall back to PS256
json = (new ClientProfilesBuilder()).addProfile( json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forsta Profilen", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forsta Profilen")
.addExecutor(SecureSigningAlgorithmEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureSigningAlgorithmEnforceExecutorFactory.PROVIDER_ID,
createSecureSigningAlgorithmEnforceExecutorConfig(org.keycloak.crypto.Algorithm.RS512)) createSecureSigningAlgorithmEnforceExecutorConfig(org.keycloak.crypto.Algorithm.RS512))
.toRepresentation() .toRepresentation()
@ -1318,7 +1318,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// update profiles, enforce ES256 // update profiles, enforce ES256
json = (new ClientProfilesBuilder()).addProfile( json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forsta Profilen", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forsta Profilen")
.addExecutor(SecureSigningAlgorithmEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureSigningAlgorithmEnforceExecutorFactory.PROVIDER_ID,
createSecureSigningAlgorithmEnforceExecutorConfig(org.keycloak.crypto.Algorithm.ES256)) createSecureSigningAlgorithmEnforceExecutorConfig(org.keycloak.crypto.Algorithm.ES256))
.toRepresentation() .toRepresentation()
@ -1343,7 +1343,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testSecureClientRegisteringUriEnforceExecutor() throws Exception { public void testSecureClientRegisteringUriEnforceExecutor() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Ensimmainen Profiili", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Ensimmainen Profiili")
.addExecutor(SecureClientRegisteringUriEnforceExecutorFactory.PROVIDER_ID, null) .addExecutor(SecureClientRegisteringUriEnforceExecutorFactory.PROVIDER_ID, null)
.toRepresentation() .toRepresentation()
).toString(); ).toString();
@ -1351,7 +1351,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Ensimmainen Politiikka", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Ensimmainen Politiikka", Boolean.TRUE)
.addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID,
createClientUpdateContextConditionConfig(Arrays.asList( createClientUpdateContextConditionConfig(Arrays.asList(
ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER, ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER,
@ -1388,6 +1388,18 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
}); });
assertEquals(false, getClientByAdmin(cid).isServiceAccountsEnabled()); assertEquals(false, getClientByAdmin(cid).isServiceAccountsEnabled());
// update policies
json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Paivitetyn Ensimmaisen Politiikka", Boolean.TRUE)
.addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID,
createClientUpdateContextConditionConfig(Arrays.asList(
ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER,
ClientUpdateContextConditionFactory.BY_REGISTRATION_ACCESS_TOKEN)))
.addProfile(PROFILE_NAME)
.toRepresentation()
).toString();
updatePolicies(json);
try { try {
updateClientDynamically(clientId, (OIDCClientRepresentation clientRep) -> { updateClientDynamically(clientId, (OIDCClientRepresentation clientRep) -> {
clientRep.setRedirectUris(Collections.singletonList("https://newredirect/*")); clientRep.setRedirectUris(Collections.singletonList("https://newredirect/*"));
@ -1531,10 +1543,11 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testSecureSigningAlgorithmForSignedJwtEnforceExecutorWithSecureAlg() throws Exception { public void testSecureSigningAlgorithmForSignedJwtEnforceExecutorWithSecureAlg() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Ensimmainen Profiili", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Ensimmainen Profiili")
.addExecutor(SecureSigningAlgorithmForSignedJwtEnforceExecutorFactory.PROVIDER_ID, createSecureSigningAlgorithmForSignedJwtEnforceExecutorConfig(Boolean.FALSE)) .addExecutor(SecureSigningAlgorithmForSignedJwtEnforceExecutorFactory.PROVIDER_ID, createSecureSigningAlgorithmForSignedJwtEnforceExecutorConfig(Boolean.TRUE)
.toRepresentation() ).toRepresentation()
).toString(); )
.toString();
updateProfiles(json); updateProfiles(json);
// register policies // register policies
@ -1542,7 +1555,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
String roleZetaName = "sample-client-role-zeta"; String roleZetaName = "sample-client-role-zeta";
String roleCommonName = "sample-client-role-common"; String roleCommonName = "sample-client-role-common";
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Den Forste Politikken", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Den Forste Politikken", Boolean.TRUE)
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList(roleAlphaName, roleZetaName))) createClientRolesConditionConfig(Arrays.asList(roleAlphaName, roleZetaName)))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -1622,7 +1635,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testSecureSigningAlgorithmForSignedJwtEnforceExecutorWithNotSecureAlg() throws Exception { public void testSecureSigningAlgorithmForSignedJwtEnforceExecutorWithNotSecureAlg() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Ensimmainen Profiili", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Ensimmainen Profiili")
.addExecutor(SecureSigningAlgorithmForSignedJwtEnforceExecutorFactory.PROVIDER_ID, createSecureSigningAlgorithmForSignedJwtEnforceExecutorConfig(Boolean.FALSE)) .addExecutor(SecureSigningAlgorithmForSignedJwtEnforceExecutorFactory.PROVIDER_ID, createSecureSigningAlgorithmForSignedJwtEnforceExecutorConfig(Boolean.FALSE))
.toRepresentation() .toRepresentation()
).toString(); ).toString();
@ -1633,7 +1646,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
String roleZetaName = "sample-client-role-zeta"; String roleZetaName = "sample-client-role-zeta";
String roleCommonName = "sample-client-role-common"; String roleCommonName = "sample-client-role-common";
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Den Forste Politikken", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Den Forste Politikken", Boolean.TRUE)
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList(roleAlphaName, roleZetaName))) createClientRolesConditionConfig(Arrays.asList(roleAlphaName, roleZetaName)))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -1683,7 +1696,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Az Elso Profil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Az Elso Profil")
.addExecutor(HolderOfKeyEnforceExecutorFactory.PROVIDER_ID, .addExecutor(HolderOfKeyEnforceExecutorFactory.PROVIDER_ID,
createHolderOfKeyEnforceExecutorConfig(Boolean.TRUE)) createHolderOfKeyEnforceExecutorConfig(Boolean.TRUE))
.addExecutor(SecureSigningAlgorithmForSignedJwtEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureSigningAlgorithmForSignedJwtEnforceExecutorFactory.PROVIDER_ID,
@ -1694,7 +1707,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Az Elso Politika", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Az Elso Politika", Boolean.TRUE)
.addCondition(AnyClientConditionFactory.PROVIDER_ID, .addCondition(AnyClientConditionFactory.PROVIDER_ID,
createAnyClientConditionConfig()) createAnyClientConditionConfig())
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -1715,7 +1728,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testNegativeLogicCondition() throws Exception { public void testNegativeLogicCondition() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forste Profilen", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forste Profilen")
.addExecutor(SecureSessionEnforceExecutorFactory.PROVIDER_ID, null) .addExecutor(SecureSessionEnforceExecutorFactory.PROVIDER_ID, null)
.toRepresentation() .toRepresentation()
).toString(); ).toString();
@ -1723,7 +1736,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Premiere Politique", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Premiere Politique", Boolean.TRUE)
.addCondition(AnyClientConditionFactory.PROVIDER_ID, createAnyClientConditionConfig()) .addCondition(AnyClientConditionFactory.PROVIDER_ID, createAnyClientConditionConfig())
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
.toRepresentation() .toRepresentation()
@ -1740,7 +1753,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
failLoginWithoutSecureSessionParameter(clientId, ERR_MSG_MISSING_NONCE); failLoginWithoutSecureSessionParameter(clientId, ERR_MSG_MISSING_NONCE);
// update policies // update policies
updatePolicy((new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Premiere Politique", Boolean.FALSE, Boolean.TRUE, null, null) updatePolicy((new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Premiere Politique", Boolean.TRUE)
.addCondition(AnyClientConditionFactory.PROVIDER_ID, createAnyClientConditionConfig(Boolean.TRUE)) .addCondition(AnyClientConditionFactory.PROVIDER_ID, createAnyClientConditionConfig(Boolean.TRUE))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
.toRepresentation()); .toRepresentation());
@ -1748,7 +1761,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
successfulLoginAndLogout(clientId, clientSecret); successfulLoginAndLogout(clientId, clientSecret);
// update policies // update policies
updatePolicy((new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Premiere Politique", Boolean.FALSE, Boolean.TRUE, null, null) updatePolicy((new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Premiere Politique", Boolean.TRUE)
.addCondition(AnyClientConditionFactory.PROVIDER_ID, createAnyClientConditionConfig(Boolean.FALSE)) .addCondition(AnyClientConditionFactory.PROVIDER_ID, createAnyClientConditionConfig(Boolean.FALSE))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
.toRepresentation()); .toRepresentation());
@ -1763,7 +1776,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testExtendedClientPolicyIntefacesForClientRegistrationPolicyMigration() throws Exception { public void testExtendedClientPolicyIntefacesForClientRegistrationPolicyMigration() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forste Profilen", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forste Profilen")
.addExecutor(TestRaiseExeptionExecutorFactory.PROVIDER_ID, null) .addExecutor(TestRaiseExeptionExecutorFactory.PROVIDER_ID, null)
.toRepresentation() .toRepresentation()
).toString(); ).toString();
@ -1771,7 +1784,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Premiere Politique", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "La Premiere Politique", Boolean.TRUE)
.addCondition(AnyClientConditionFactory.PROVIDER_ID, createAnyClientConditionConfig()) .addCondition(AnyClientConditionFactory.PROVIDER_ID, createAnyClientConditionConfig())
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
.toRepresentation() .toRepresentation()
@ -1811,36 +1824,19 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
} }
@Test @Test
public void testOverwriteBuiltinProfileNotAllowed() throws Exception { public void testUpdatePolicyWithoutNameNotAllowed() throws Exception {
// register profiles
String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile("builtin-default-profile", "Pershyy Profil", Boolean.FALSE, null)
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID,
createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE,
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID),
X509ClientAuthenticator.PROVIDER_ID))
.toRepresentation()
).toString();
try {
updateProfiles(json);
} catch (ClientPolicyException cpe) {
assertEquals("update profiles failed", cpe.getError());
}
}
@Test
public void testUpdatePolicyWithoutNameNotAllowd() throws Exception {
// register policies // register policies
String json = (new ClientPoliciesBuilder()).addPolicy( String json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(null, "La Premiere Politique", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(null, "La Premiere Politique", Boolean.TRUE)
.addCondition(AnyClientConditionFactory.PROVIDER_ID, createAnyClientConditionConfig()) .addCondition(AnyClientConditionFactory.PROVIDER_ID, createAnyClientConditionConfig())
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
.toRepresentation() .toRepresentation()
).toString(); ).toString();
try { try {
updatePolicies(json); updatePolicies(json);
fail();
} catch (ClientPolicyException cpe) { } catch (ClientPolicyException cpe) {
assertEquals("update profiles failed", cpe.getError()); assertEquals("update policies failed", cpe.getError());
} }
} }
@ -1848,7 +1844,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testConfidentialClientAcceptExecutorExecutor() throws Exception { public void testConfidentialClientAcceptExecutorExecutor() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Erstes Profil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Erstes Profil")
.addExecutor(ConfidentialClientAcceptExecutorFactory.PROVIDER_ID, null) .addExecutor(ConfidentialClientAcceptExecutorFactory.PROVIDER_ID, null)
.toRepresentation() .toRepresentation()
).toString(); ).toString();
@ -1856,7 +1852,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Erstes Politik", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Erstes Politik", Boolean.TRUE)
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE))) createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE)))
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -1897,7 +1893,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
public void testConsentRequiredExecutorExecutor() throws Exception { public void testConsentRequiredExecutorExecutor() throws Exception {
// register profiles // register profiles
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Test Profile", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Test Profile")
.addExecutor(ConsentRequiredExecutorFactory.PROVIDER_ID, null) .addExecutor(ConsentRequiredExecutorFactory.PROVIDER_ID, null)
.toRepresentation() .toRepresentation()
).toString(); ).toString();
@ -1905,7 +1901,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Test Policy", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Test Policy", Boolean.TRUE)
.addCondition(AnyClientConditionFactory.PROVIDER_ID, .addCondition(AnyClientConditionFactory.PROVIDER_ID,
createAnyClientConditionConfig()) createAnyClientConditionConfig())
.addProfile(PROFILE_NAME) .addProfile(PROFILE_NAME)
@ -2064,7 +2060,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register profiles // register profiles
String profileName = "MyProfile"; String profileName = "MyProfile";
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(profileName, "Primum Profile", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(profileName, "Primum Profile")
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID,
createSecureClientAuthEnforceExecutorConfig(Boolean.FALSE, createSecureClientAuthEnforceExecutorConfig(Boolean.FALSE,
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID), Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID),
@ -2075,7 +2071,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(policyName, "Primum Consilium", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(policyName, "Primum Consilium", Boolean.TRUE)
.addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID,
createClientUpdateContextConditionConfig(Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER))) createClientUpdateContextConditionConfig(Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER)))
.addProfile(profileName) .addProfile(profileName)
@ -2088,7 +2084,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register profiles // register profiles
String profileName = "MyProfile"; String profileName = "MyProfile";
String json = (new ClientProfilesBuilder()).addProfile( String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(profileName, "Primul Profil", Boolean.FALSE, null) (new ClientProfileBuilder()).createProfile(profileName, "Primul Profil")
.addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, .addExecutor(SecureClientAuthEnforceExecutorFactory.PROVIDER_ID,
createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE, createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE,
Arrays.asList(ClientIdAndSecretAuthenticator.PROVIDER_ID, JWTClientAuthenticator.PROVIDER_ID), Arrays.asList(ClientIdAndSecretAuthenticator.PROVIDER_ID, JWTClientAuthenticator.PROVIDER_ID),
@ -2101,7 +2097,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
// register policies // register policies
json = (new ClientPoliciesBuilder()).addPolicy( json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(policyName, "Prima Politica", Boolean.FALSE, Boolean.TRUE, null, null) (new ClientPolicyBuilder()).createPolicy(policyName, "Prima Politica", Boolean.TRUE)
.addCondition(ClientRolesConditionFactory.PROVIDER_ID, .addCondition(ClientRolesConditionFactory.PROVIDER_ID,
createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE))) createClientRolesConditionConfig(Arrays.asList(SAMPLE_CLIENT_ROLE)))
.addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID, .addCondition(ClientUpdateContextConditionFactory.PROVIDER_ID,

View file

@ -16,7 +16,7 @@ public class InternalComponentRepresentation implements FetchOnServerWrapper<Com
@Override @Override
public FetchOnServer getRunOnServer() { public FetchOnServer getRunOnServer() {
return (FetchOnServer) session -> ModelToRepresentation.toRepresentation(session.getContext().getRealm(), true); return (FetchOnServer) session -> ModelToRepresentation.toRepresentation(session, session.getContext().getRealm(), true);
} }
@Override @Override

View file

@ -53,7 +53,7 @@ public class RunOnServerTest extends AbstractKeycloakTest {
RealmRepresentation realmRep = testingClient.server().fetch(session -> { RealmRepresentation realmRep = testingClient.server().fetch(session -> {
RealmModel master = session.realms().getRealm(realmName); RealmModel master = session.realms().getRealm(realmName);
return ModelToRepresentation.toRepresentation(master, true); return ModelToRepresentation.toRepresentation(session, master, true);
}, RealmRepresentation.class); }, RealmRepresentation.class);
assertEquals(realmName, realmRep.getRealm()); assertEquals(realmName, realmRep.getRealm());

View file

@ -96,3 +96,6 @@ log4j.logger.org.keycloak.services.clientregistration.policy=debug
# Enable to log short stack traces for log entries enabled with StackUtil.getShortStackTrace() calls # Enable to log short stack traces for log entries enabled with StackUtil.getShortStackTrace() calls
# log4j.logger.org.keycloak.STACK_TRACE=trace # log4j.logger.org.keycloak.STACK_TRACE=trace
# Client policies
#log4j.logger.org.keycloak.services.clientpolicy=trace

View file

@ -110,3 +110,6 @@ log4j.logger.org.apache.directory.server.ldap.LdapProtocolHandler=error
#log4j.logger.org.keycloak.credential.WebAuthnCredentialProvider=debug #log4j.logger.org.keycloak.credential.WebAuthnCredentialProvider=debug
#log4j.logger.org.keycloak.authentication.requiredactions.WebAuthnRegister=debug #log4j.logger.org.keycloak.authentication.requiredactions.WebAuthnRegister=debug
#log4j.logger.org.keycloak.authentication.authenticators.browser.WebAuthnAuthenticator=debug #log4j.logger.org.keycloak.authentication.authenticators.browser.WebAuthnAuthenticator=debug
# Client policies
#log4j.logger.org.keycloak.services.clientpolicy=trace

View file

@ -844,6 +844,46 @@ max-clients.tooltip=It will not be allowed to register a new client if count of
client-scopes=Client Scopes client-scopes=Client Scopes
client-scopes.tooltip=Client scopes allow you to define a common set of protocol mappers and roles, which are shared between multiple clients client-scopes.tooltip=Client scopes allow you to define a common set of protocol mappers and roles, which are shared between multiple clients
# Client Policies
realm-tab-client-policies=Client Policies
client-policies-profiles=Profiles
client-policies-profiles.tooltip=Client Profile allows to setup set of executors, which are enforced for various actions done with the client. Actions can be admin actions like creating or updating client, or user actions like authentication to the client.
client-policies-policies=Policies
client-policies-policies.tooltip=Client Policy allows to bind client profiles with various conditions to specify when exactly is enforced behaviour specified by executors of the particular client profile.
client-profiles-form-view=Form View
client-profiles-json-editor=JSON Editor
global=Global
executors=Executors
client-profile-name.tooltip=Name of the client profile. Must be unique within the realm
client-profile-executors.tooltip=Executors, which will be applied for this client profile
no-executors-available=No Executors Available
push-profile-to-json=Push Profile to JSON
executor-type=Executor Type
create-executor=Create Executor
client-policy-name.tooltip=Name of the client policy. Must be unique within the realm.
client-policy-enabled.tooltip=Specifies if client policy is enabled. Disabled policies are not considered at all during evaluation of client requests.
conditions=Conditions
client-policy-conditions.tooltip=Conditions, which will be evaluated to determine if client policy should be applied during particular action or not.
no-conditions-available=No Conditions Available
condition-type=Condition Type
create-condition=Create Condition
client-profiles=Client Profiles
client-profiles.tooltip=Client Profiles applied on this policy
add-profile.placeholder=Add client profile ...
no-client-profiles-configured=No client profiles configured
clientscopes-condition.label=Expected Scopes
clientscopes-condition.tooltip=The list of expected client scopes. Condition evaluates to true if specified client request matches some of the client scopes. It depends also whether it should be default or optional client scope based on the 'Scope Type' configured.
client-accesstype.label=Client Access Type
client-accesstype.tooltip=Access Type of the client, for which the condition will be applied.
clientroles-condition.label=Client Roles
clientroles-condition.tooltip=Client roles, which will be checked during this condition evaluation. Condition evaluates to true if client has at least one client role with the name as the client roles specified in the configuration.
clientupdatesourcegroups-condition.label=Groups
clientupdatesourcegroups-condition.tooltip=Name of groups to check. Condition evaluates to true if the entity, who creates/updates client is member of some of the specified groups. Configured groups are specified by their simple name, which must match to the name of the Keycloak group. No support for group hierarchy is used here.
clientupdate-trusted-hosts.label=Trusted hosts
clientupdate-trusted-hosts.tooltip=List of Hosts, which are trusted. In case that client registration/update request comes from the host/domain specified in this configuration, condition evaluates to true. You can use hostnames or IP addresses. If you use star at the beginning (for example '*.example.com' ) then whole domain example.com will be trusted.
clientupdatesourceroles-condition.label=Updating entity role
clientupdatesourceroles-condition.tooltip=The condition is checked during client registration/update requests and it evaluates to true if the entity (usually user), who is creating/updating client is member of the specified role. For reference the realm role, you can use the realm role name like 'my_realm_role' . For reference client role, you can use the client_id.role_name for example 'my_client.my_client_role' will refer to client role 'my_client_role' of client 'my_client'.
groups=Groups groups=Groups

View file

@ -330,6 +330,168 @@ module.config([ '$routeProvider', function($routeProvider) {
}, },
controller : 'ClientRegPolicyDetailCtrl' controller : 'ClientRegPolicyDetailCtrl'
}) })
.when('/realms/:realm/client-policies/profiles', {
templateUrl : resourceUrl + '/partials/client-policies-profiles-list.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
clientProfiles : function(ClientPoliciesProfilesLoader) {
return ClientPoliciesProfilesLoader.loadClientProfiles('true');
},
},
controller : 'ClientPoliciesProfilesListCtrl'
})
.when('/realms/:realm/client-policies/profiles-json', {
templateUrl : resourceUrl + '/partials/client-policies-profiles-json.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
clientProfiles : function(ClientPoliciesProfilesLoader) {
return ClientPoliciesProfilesLoader.loadClientProfiles('false');
}
},
controller : 'ClientPoliciesProfilesJsonCtrl'
})
.when('/realms/:realm/client-policies/profiles-create', {
templateUrl : resourceUrl + '/partials/client-policies-profiles-edit.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
clientProfiles : function(ClientPoliciesProfilesLoader) {
return ClientPoliciesProfilesLoader.loadClientProfiles('false');
}
},
controller : 'ClientPoliciesProfilesEditCtrl'
})
.when('/realms/:realm/client-policies/profiles-update/:profileName', {
templateUrl : resourceUrl + '/partials/client-policies-profiles-edit.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
clientProfiles : function(ClientPoliciesProfilesLoader) {
return ClientPoliciesProfilesLoader.loadClientProfiles('true');
}
},
controller : 'ClientPoliciesProfilesEditCtrl'
})
.when('/realms/:realm/client-policies/profiles-update/:profileName/create-executor', {
templateUrl : resourceUrl + '/partials/client-policies-profiles-edit-executor.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
clientProfiles : function(ClientPoliciesProfilesLoader) {
return ClientPoliciesProfilesLoader.loadClientProfiles('false');
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
controller : 'ClientPoliciesProfilesEditExecutorCtrl'
})
.when('/realms/:realm/client-policies/profiles-update/:profileName/update-executor/:executorIndex', {
templateUrl : resourceUrl + '/partials/client-policies-profiles-edit-executor.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
clientProfiles : function(ClientPoliciesProfilesLoader) {
return ClientPoliciesProfilesLoader.loadClientProfiles('true');
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
controller : 'ClientPoliciesProfilesEditExecutorCtrl'
})
.when('/realms/:realm/client-policies/policies', {
templateUrl : resourceUrl + '/partials/client-policies-list.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
clientPolicies : function(ClientPoliciesLoader) {
return ClientPoliciesLoader();
}
},
controller : 'ClientPoliciesListCtrl'
})
.when('/realms/:realm/client-policies/policies-json', {
templateUrl : resourceUrl + '/partials/client-policies-json.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
clientPolicies : function(ClientPoliciesLoader) {
return ClientPoliciesLoader();
}
},
controller : 'ClientPoliciesJsonCtrl'
})
.when('/realms/:realm/client-policies/policy-create', {
templateUrl : resourceUrl + '/partials/client-policies-policy-edit.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
clientProfiles : function(ClientPoliciesProfilesLoader) {
return ClientPoliciesProfilesLoader.loadClientProfiles('true');
},
clientPolicies : function(ClientPoliciesLoader) {
return ClientPoliciesLoader();
}
},
controller : 'ClientPoliciesEditCtrl'
})
.when('/realms/:realm/client-policies/policies-update/:policyName', {
templateUrl : resourceUrl + '/partials/client-policies-policy-edit.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
clientProfiles : function(ClientPoliciesProfilesLoader) {
return ClientPoliciesProfilesLoader.loadClientProfiles('true');
},
clientPolicies : function(ClientPoliciesLoader) {
return ClientPoliciesLoader();
}
},
controller : 'ClientPoliciesEditCtrl'
})
.when('/realms/:realm/client-policies/policies-update/:policyName/create-condition', {
templateUrl : resourceUrl + '/partials/client-policies-policy-edit-condition.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
clientPolicies : function(ClientPoliciesLoader) {
return ClientPoliciesLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
controller : 'ClientPoliciesEditConditionCtrl'
})
.when('/realms/:realm/client-policies/policies-update/:policyName/update-condition/:conditionIndex', {
templateUrl : resourceUrl + '/partials/client-policies-policy-edit-condition.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
clientPolicies : function(ClientPoliciesLoader) {
return ClientPoliciesLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
controller : 'ClientPoliciesEditConditionCtrl'
})
.when('/realms/:realm/keys', { .when('/realms/:realm/keys', {
templateUrl : resourceUrl + '/partials/realm-keys.html', templateUrl : resourceUrl + '/partials/realm-keys.html',
resolve : { resolve : {

View file

@ -2966,6 +2966,655 @@ module.controller('ClientRegPolicyDetailCtrl', function ($scope, realm, clientRe
}); });
module.controller('ClientPoliciesProfilesListCtrl', function($scope, realm, clientProfiles, ClientPoliciesProfiles, Dialog, Notifications, $route, $location) {
console.log('ClientPoliciesProfilesListCtrl');
$scope.realm = realm;
$scope.clientProfiles = clientProfiles;
$scope.removeClientProfile = function(clientProfile) {
console.log("Deleting client profile from the JSON: " + clientProfile.name);
for (var i=0 ; i < $scope.clientProfiles.profiles.length ; i++) {
var currentProfile = $scope.clientProfiles.profiles[i];
if (currentProfile.name === clientProfile.name) {
$scope.clientProfiles.profiles.splice(i, 1);
break;
}
}
ClientPoliciesProfiles.update({
realm: realm.realm,
}, $scope.clientProfiles, function () {
$route.reload();
Notifications.success("The client profile was deleted.");
}, function(errorResponse) {
Notifications.error('Failed to delete client profile. Check server log for the details');
});
};
});
module.controller('ClientPoliciesProfilesJsonCtrl', function($scope, realm, clientProfiles, ClientPoliciesProfiles, Dialog, Notifications, $route, $location) {
console.log('ClientPoliciesProfilesJsonCtrl');
$scope.realm = realm;
$scope.clientProfilesString = angular.toJson(clientProfiles, true);
$scope.save = function() {
var clientProfilesObj = null;
try {
var clientProfilesObj = angular.fromJson($scope.clientProfilesString);
} catch (e) {
Notifications.error("Provided JSON is incorrect. See browser javascript console for the details");
console.log(e);
return;
}
var clientProfilesCompressed = angular.toJson(clientProfilesObj, false);
ClientPoliciesProfiles.update({
realm: realm.realm,
}, clientProfilesCompressed, function () {
$route.reload();
Notifications.success("The client profiles configuration was updated.");
}, function(errorResponse) {
Notifications.error('Failed to update client profiles. Check browser javascript console and server log for the details');
console.log("Error response when updating client profiles JSON: Status: " + errorResponse.status +
", statusText: " + errorResponse.statusText + ", data: " + errorResponse.data);
});
};
$scope.reset = function() {
$route.reload();
};
});
module.controller('ClientPoliciesProfilesEditCtrl', function($scope, realm, clientProfiles, ClientPoliciesProfiles, Dialog, Notifications, $route, $location) {
var targetProfileName = $route.current.params.profileName;
$scope.createNew = targetProfileName == null;
if ($scope.createNew) {
console.log('ClientPoliciesProfilesEditCtrl: creating new profile');
} else {
console.log('ClientPoliciesProfilesEditCtrl: updating profile ' + targetProfileName);
}
$scope.realm = realm;
$scope.editedProfile = null;
function getProfileByName(profilesArray) {
if (!profilesArray) return null;
for (var i=0 ; i < profilesArray.length ; i++) {
var currentProfile = profilesArray[i];
if (targetProfileName === currentProfile.name) {
return currentProfile;
}
}
}
if ($scope.createNew) {
$scope.editedProfile = {
name: "",
executors: []
};
} else {
var globalProfile = false;
$scope.editedProfile = getProfileByName(clientProfiles.profiles);
if (!$scope.editedProfile) {
$scope.editedProfile = getProfileByName(clientProfiles.globalProfiles);
globalProfile = true;
}
if ($scope.editedProfile == null) {
console.log("Profile of name " + targetProfileName + " not found");
throw 'Profile not found';
}
}
$scope.readOnly = !$scope.access.manageRealm || globalProfile;
$scope.removeExecutor = function(executorIndex) {
console.log("remove executor of index " + executorIndex);
// Delete executor
$scope.editedProfile.executors.splice(executorIndex, 1);
ClientPoliciesProfiles.update({
realm: realm.realm,
}, clientProfiles, function () {
Notifications.success("The executor was deleted.");
}, function(errorResponse) {
Notifications.error('Failed to delete executor. Check server log for the details');
});
}
$scope.save = function() {
if (!$scope.editedProfile.name || $scope.editedProfile.name === '') {
Notifications.error('Name must be provided');
return;
}
if ($scope.createNew) {
clientProfiles.profiles.push($scope.editedProfile);
}
ClientPoliciesProfiles.update({
realm: realm.realm,
}, clientProfiles, function () {
if ($scope.createNew) {
Notifications.success("The client profile was created.");
$location.url('/realms/' + realm.realm + '/client-policies/profiles-update/' + $scope.editedProfile.name);
} else {
Notifications.success("The client profile was updated.");
$location.url('/realms/' + realm.realm + '/client-policies/profiles');
}
}, function(errorResponse) {
if ($scope.createNew) {
Notifications.error('Failed to create client profile. Check server log for the details');
} else {
Notifications.error('Failed to update client profile. Check server log for the details');
}
});
};
$scope.back = function() {
$location.url('/realms/' + realm.realm + '/client-policies/profiles');
};
});
module.controller('ClientPoliciesProfilesEditExecutorCtrl', function($scope, realm, serverInfo, clientProfiles, ClientPoliciesProfiles, ComponentUtils, Dialog, Notifications, $route, $location) {
var updatedExecutorIndex = $route.current.params.executorIndex;
var targetProfileName = $route.current.params.profileName;
$scope.createNew = updatedExecutorIndex == null;
if ($scope.createNew) {
console.log('ClientPoliciesProfilesEditExecutorCtrl: adding executor to profile ' + targetProfileName);
} else {
console.log('ClientPoliciesProfilesEditExecutorCtrl: updating executor with index ' + updatedExecutorIndex + ' of profile ' + targetProfileName);
}
$scope.realm = realm;
function getProfileByName(profilesArray) {
if (!profilesArray) return null;
for (var i=0 ; i < profilesArray.length ; i++) {
var currentProfile = profilesArray[i];
if (targetProfileName === currentProfile.name) {
return currentProfile;
}
}
}
var globalProfile = false;
var editedProfile = getProfileByName(clientProfiles.profiles);
if (!editedProfile) {
editedProfile = getProfileByName(clientProfiles.globalProfiles);
globalProfile = true;
}
if (editedProfile == null) {
throw 'Client profile of specified name not found';
}
$scope.readOnly = !$scope.access.manageRealm || globalProfile;
$scope.executorTypes = serverInfo.componentTypes['org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider'];
for (var j=0 ; j < $scope.executorTypes.length ; j++) {
var currExecutorType = $scope.executorTypes[j];
if (currExecutorType.properties) {
console.log("Adjusting executorType: " + currExecutorType.id);
ComponentUtils.addMvOptionsToMultivaluedLists(currExecutorType.properties);
}
}
function getExecutorByIndex(clientProfile, executorIndex) {
if (clientProfile.executors.length <= executorIndex) {
console.error('Client profile does not have executor of specified index');
$location.path('/notfound');
return null;
} else {
return clientProfile.executors[executorIndex];
}
}
if ($scope.createNew) {
// make first type the default
$scope.executorType = $scope.executorTypes[0];
var oldExecutorType = $scope.executorType;
initConfig();
$scope.$watch('executorType', function() {
if (!angular.equals($scope.executorType, oldExecutorType)) {
oldExecutorType = $scope.executorType;
initConfig();
}
}, true);
} else {
var exec = getExecutorByIndex(editedProfile, updatedExecutorIndex);
if (exec) {
$scope.executor = {
config: exec.configuration
};
$scope.executorType = null;
for (var j=0 ; j < $scope.executorTypes.length ; j++) {
var currentExType = $scope.executorTypes[j];
if (exec.executor === currentExType.id) {
$scope.executorType = currentExType;
break;
}
}
}
}
function toDefaultValue(configProperty) {
if (configProperty.type === 'MultivaluedString' || configProperty.type === 'MultivaluedList') {
if (configProperty.defaultValue) {
return configProperty.defaultValue;
} else {
return [];
}
}
if (configProperty.defaultValue !== undefined) {
return configProperty.defaultValue;
} else {
return null;
}
}
function initConfig() {
console.log("Initialized config now. ConfigType is: " + $scope.executorType.id);
$scope.executor = {
config: {}
};
for (let i = 0; i < $scope.executorType.properties.length; i++) {
let configProperty = $scope.executorType.properties[i];
$scope.executor.config[configProperty.name] = toDefaultValue(configProperty);
}
}
$scope.save = function() {
console.log("save: " + $scope.executorType.id);
var executorName = $scope.executorType.id;
if (!editedProfile.executors) {
editedProfile.executors = [];
}
ComponentUtils.removeLastEmptyValue($scope.executor.config);
if ($scope.createNew) {
var selectedExecutor = {
executor: $scope.executorType.id,
configuration: $scope.executor.config
};
editedProfile.executors.push(selectedExecutor);
} else {
var currentExecutor = getExecutorByIndex(editedProfile, updatedExecutorIndex);
if (currentExecutor) {
currentExecutor.configuration = $scope.executor.config;
}
}
ClientPoliciesProfiles.update({
realm: realm.realm,
}, clientProfiles, function () {
if ($scope.createNew) {
Notifications.success("Executor created successfully");
} else {
Notifications.success("Executor updated successfully");
}
$location.url('/realms/' + realm.realm + '/client-policies/profiles-update/' + editedProfile.name);
});
};
$scope.cancel = function() {
$location.url('/realms/' + realm.realm + '/client-policies/profiles-update/' + editedProfile.name);
};
});
module.controller('ClientPoliciesListCtrl', function($scope, realm, clientPolicies, ClientPolicies, Dialog, Notifications, $route, $location) {
console.log('ClientPoliciesListCtrl');
$scope.realm = realm;
$scope.clientPolicies = clientPolicies;
$scope.removeClientPolicy = function(clientPolicy) {
console.log("Deleting client policy from the JSON: " + clientPolicy.name);
for (var i=0 ; i < $scope.clientPolicies.policies.length ; i++) {
var currentPolicy = $scope.clientPolicies.policies[i];
if (currentPolicy.name === clientPolicy.name) {
$scope.clientPolicies.policies.splice(i, 1);
break;
}
}
ClientPolicies.update({
realm: realm.realm,
}, $scope.clientPolicies, function () {
$route.reload();
Notifications.success("The client policy was deleted.");
}, function(errorResponse) {
Notifications.error('Failed to delete client policy. Check server log for the details');
});
};
});
module.controller('ClientPoliciesJsonCtrl', function($scope, realm, clientPolicies, Dialog, Notifications, ClientPolicies, $route, $location) {
console.log('ClientPoliciesJsonCtrl');
$scope.realm = realm;
$scope.clientPoliciesString = angular.toJson(clientPolicies, true);
$scope.save = function() {
var clientPoliciesObj = null;
try {
var clientPoliciesObj = angular.fromJson($scope.clientPoliciesString);
} catch (e) {
Notifications.error("Provided JSON is incorrect. See browser javascript console for the details");
console.log(e);
return;
}
var clientPoliciesCompressed = angular.toJson(clientPoliciesObj, false);
ClientPolicies.update({
realm: realm.realm,
}, clientPoliciesCompressed, function () {
$route.reload();
Notifications.success("The client policies configuration was updated.");
}, function(errorResponse) {
Notifications.error('Failed to update client policies. Check browser javascript console and server log for the details');
console.log("Error response when updating client policies JSON: Status: " + errorResponse.status +
", statusText: " + errorResponse.statusText + ", data: " + errorResponse.data);
});
};
$scope.reset = function() {
$route.reload();
};
});
module.controller('ClientPoliciesEditCtrl', function($scope, realm, clientProfiles, clientPolicies, ClientPolicies, Dialog, Notifications, $route, $location) {
var targetPolicyName = $route.current.params.policyName;
$scope.createNew = targetPolicyName == null;
if ($scope.createNew) {
console.log('ClientPoliciesEditCtrl: creating new policy');
} else {
console.log('ClientPoliciesEditCtrl: updating policy ' + targetPolicyName);
}
$scope.realm = realm;
$scope.clientPolicies = clientPolicies;
$scope.clientProfiles = clientProfiles;
$scope.editedPolicy = null;
if ($scope.createNew) {
$scope.editedPolicy = {
name: "",
enabled: true,
profiles: [],
conditions: []
};
} else {
for (var i=0 ; i < $scope.clientPolicies.policies.length ; i++) {
var currentPolicy = $scope.clientPolicies.policies[i];
if (targetPolicyName === currentPolicy.name) {
$scope.editedPolicy = currentPolicy;
break;
}
}
if ($scope.editedPolicy == null) {
console.log("Policy of name " + targetPolicyName + " not found");
throw 'Policy not found';
}
}
$scope.readOnly = !$scope.access.manageRealm;
$scope.availableProfiles = [];
var allClientProfiles = clientProfiles.profiles;
if (clientProfiles.globalProfiles) {
allClientProfiles = allClientProfiles.concat(clientProfiles.globalProfiles);
}
for (var k=0 ; k<allClientProfiles.length ; k++) {
var profileName = allClientProfiles[k].name;
if (!$scope.editedPolicy.profiles || !$scope.editedPolicy.profiles.includes(profileName)) {
$scope.availableProfiles.push(profileName);
}
}
$scope.removeCondition = function(conditionIndex) {
console.log("remove condition of index " + conditionIndex);
// Delete condition
$scope.editedPolicy.conditions.splice(conditionIndex, 1);
ClientPolicies.update({
realm: realm.realm,
}, $scope.clientPolicies, function () {
Notifications.success("The condition was deleted.");
}, function(errorResponse) {
Notifications.error('Failed to delete condition. Check server log for the details');
});
}
$scope.save = function() {
if (!$scope.editedPolicy.name || $scope.editedPolicy.name === '') {
Notifications.error('Name must be provided');
return;
}
if ($scope.createNew) {
$scope.clientPolicies.policies.push($scope.editedPolicy);
}
ClientPolicies.update({
realm: realm.realm,
}, $scope.clientPolicies, function () {
if ($scope.createNew) {
Notifications.success("The client policy was created.");
$location.url('/realms/' + realm.realm + '/client-policies/policies-update/' + $scope.editedPolicy.name);
} else {
Notifications.success("The client policy was updated.");
$location.url('/realms/' + realm.realm + '/client-policies/policies');
}
}, function(errorResponse) {
if ($scope.createNew) {
Notifications.error('Failed to create client policy. Check server log for the details');
} else {
Notifications.error('Failed to update client policy. Check server log for the details');
}
});
};
$scope.back = function() {
$location.url('/realms/' + realm.realm + '/client-policies/policies');
};
function moveProfileAndUpdatePolicy(arrayFrom, arrayTo, profileName, notificationsMessage) {
for (var i=0 ; i<arrayFrom.length ; i++) {
if (arrayFrom[i] === profileName) {
arrayFrom.splice(i, 1);
arrayTo.push(profileName);
break;
}
}
ClientPolicies.update({
realm: realm.realm,
}, $scope.clientPolicies, function () {
Notifications.success(notificationsMessage);
}, function(errorResponse) {
Notifications.error('Failed to update profiles of the policy. Check server log for the details');
});
}
$scope.addProfile = function(profileName) {
console.log("addProfile: " + profileName);
moveProfileAndUpdatePolicy($scope.availableProfiles, $scope.editedPolicy.profiles, profileName, "Profile added to the policy");
};
$scope.removeProfile = function(profileName) {
console.log("removeProfile: " + profileName);
moveProfileAndUpdatePolicy( $scope.editedPolicy.profiles, $scope.availableProfiles, profileName, "Profile removed from the policy");
}
});
module.controller('ClientPoliciesEditConditionCtrl', function($scope, realm, serverInfo, clientPolicies, ClientPolicies, Components, ComponentUtils, Dialog, Notifications, $route, $location) {
var updatedConditionIndex = $route.current.params.conditionIndex;
var targetPolicyName = $route.current.params.policyName;
$scope.createNew = updatedConditionIndex == null;
if ($scope.createNew) {
console.log('ClientPoliciesEditConditionCtrl: adding condition to policy ' + targetPolicyName);
} else {
console.log('ClientPoliciesEditConditionCtrl: updating condition with index ' + updatedConditionIndex + ' of policy ' + targetPolicyName);
}
$scope.realm = realm;
var editedPolicy = null;
for (var i=0 ; i < clientPolicies.policies.length ; i++) {
var currentPolicy = clientPolicies.policies[i];
if (targetPolicyName === currentPolicy.name) {
editedPolicy = currentPolicy;
break;
}
}
if (editedPolicy == null) {
throw 'Client policy of specified name not found';
}
$scope.readOnly = !$scope.access.manageRealm;
$scope.conditionTypes = serverInfo.componentTypes['org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProvider'];
for (var j=0 ; j < $scope.conditionTypes.length ; j++) {
var currConditionType = $scope.conditionTypes[j];
if (currConditionType.properties) {
console.log("Adjusting conditionType: " + currConditionType.id);
ComponentUtils.addMvOptionsToMultivaluedLists(currConditionType.properties);
}
}
function getConditionByIndex(clientPolicy, conditionIndex) {
if (clientPolicy.conditions.length <= conditionIndex) {
console.error('Client policy does not have condition of specified index');
$location.path('/notfound');
return null;
} else {
return clientPolicy.conditions[conditionIndex];
}
}
if ($scope.createNew) {
// make first type the default
$scope.conditionType = $scope.conditionTypes[0];
var oldConditionType = $scope.conditionType;
initConfig();
$scope.$watch('conditionType', function() {
if (!angular.equals($scope.conditionType, oldConditionType)) {
oldConditionType = $scope.conditionType;
initConfig();
}
}, true);
} else {
var cond = getConditionByIndex(editedPolicy, updatedConditionIndex);
if (cond) {
$scope.condition = {
config: cond.configuration
};
$scope.conditionType = null;
for (var j=0 ; j < $scope.conditionTypes.length ; j++) {
var currentCndType = $scope.conditionTypes[j];
if (cond.condition === currentCndType.id) {
$scope.conditionType = currentCndType;
break;
}
}
}
}
function toDefaultValue(configProperty) {
if (configProperty.type === 'MultivaluedString' || configProperty.type === 'MultivaluedList') {
if (configProperty.defaultValue) {
return configProperty.defaultValue;
} else {
return [];
}
}
if (configProperty.defaultValue !== undefined) {
return configProperty.defaultValue;
} else {
return null;
}
}
function initConfig() {
console.log("Initialized config now. ConfigType is: " + $scope.conditionType.id);
$scope.condition = {
config: {}
};
for (let i = 0; i < $scope.conditionType.properties.length; i++) {
let configProperty = $scope.conditionType.properties[i];
$scope.condition.config[configProperty.name] = toDefaultValue(configProperty);
}
}
$scope.save = function() {
console.log("save: " + $scope.conditionType.id);
var conditionName = $scope.conditionType.id;
if (!editedPolicy.conditions) {
editedPolicy.conditions = [];
}
ComponentUtils.removeLastEmptyValue($scope.condition.config);
var selectedCondition;
if ($scope.createNew) {
var selectedCondition = {
condition: $scope.conditionType.id,
configuration: $scope.condition.config
};
editedPolicy.conditions.push(selectedCondition);
} else {
var currentCondition = getConditionByIndex(editedPolicy, updatedConditionIndex);
if (currentCondition) {
currentCondition.configuration = $scope.condition.config;
}
}
ClientPolicies.update({
realm: realm.realm,
}, clientPolicies, function () {
if ($scope.createNew) {
Notifications.success("Condition created successfully");
} else {
Notifications.success("Condition updated successfully");
}
$location.url('/realms/' + realm.realm + '/client-policies/policies-update/' + editedPolicy.name);
});
};
$scope.cancel = function() {
$location.url('/realms/' + realm.realm + '/client-policies/policies-update/' + editedPolicy.name);
};
});
module.controller('RealmImportCtrl', function($scope, realm, $route, module.controller('RealmImportCtrl', function($scope, realm, $route,
Notifications, $modal, $resource) { Notifications, $modal, $resource) {
$scope.rawContent = {}; $scope.rawContent = {};

View file

@ -563,8 +563,25 @@ module.factory('ClientRegistrationPolicyProvidersLoader', function(Loader, Clien
}); });
}); });
module.factory('ClientPoliciesProfilesLoader', function(Loader, ClientPoliciesProfiles, $route , $q) {
var clientPoliciesLoader = {};
clientPoliciesLoader.loadClientProfiles = function(includeGlobalProfiles) {
return Loader.get(ClientPoliciesProfiles, function() {
return {
realm : $route.current.params.realm,
includeGlobalProfiles : includeGlobalProfiles
}
})();
};
return clientPoliciesLoader;
});
module.factory('ClientPoliciesLoader', function(Loader, ClientPolicies, $route) {
return Loader.get(ClientPolicies, function() {
return {
realm: $route.current.params.realm
}
});
});

View file

@ -2198,6 +2198,27 @@ module.factory('ClientRegistrationPolicyProviders', function($resource) {
}); });
}); });
module.factory('ClientPoliciesProfiles', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/client-policies/profiles?include-global-profiles=:includeGlobalProfiles', {
realm : '@realm',
includeGlobalProfiles : '@includeGlobalProfiles'
}, {
update : {
method : 'PUT'
}
});
});
module.factory('ClientPolicies', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/client-policies/policies', {
realm : '@realm',
}, {
update : {
method : 'PUT'
}
});
});
module.factory('LDAPMapperSync', function($resource) { module.factory('LDAPMapperSync', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/user-storage/:parentId/mappers/:mapperId/sync', { return $resource(authUrl + '/admin/realms/:realm/user-storage/:parentId/mappers/:mapperId/sync', {
realm : '@realm', realm : '@realm',

View file

@ -0,0 +1,60 @@
<!--
~ Copyright 2021 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<kc-tabs-realm></kc-tabs-realm>
<ul class="nav nav-tabs nav-tabs-pf">
<li>
<a href="#/realms/{{realm.realm}}/client-policies/profiles">{{:: 'client-policies-profiles' | translate}}</a>
<kc-tooltip>{{:: 'client-policies-profiles.tooltip' | translate}}</kc-tooltip>
</li>
<li class="active">
<a href="#/realms/{{realm.realm}}/client-policies/policies">{{:: 'client-policies-policies' | translate}}</a>
<kc-tooltip>{{:: 'client-policies-policies.tooltip' | translate}}</kc-tooltip>
</li>
</ul>
<ul class="nav nav-tabs nav-tabs-pf">
<li><a href="#/realms/{{realm.realm}}/client-policies/policies">{{:: 'client-profiles-form-view' | translate}}</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/client-policies/policies-json">{{:: 'client-profiles-json-editor' | translate}}</a></li>
</ul>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<filedset>
<div class="form-group">
<div class="col-md-10">
<div>
<textarea id="clientPoliciesConfig" name="clientPoliciesConfig" data-ng-model="clientPoliciesString" class="form-control ng-binding" rows="20" ></textarea>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<button class="btn btn-primary" data-ng-click="save()">{{:: 'save' | translate}}</button>
<button class="btn btn-default" data-ng-click="reset()">{{:: 'reset' | translate}}</button>
</div>
</div>
</filedset>
</form>
</div>
<kc-menu></kc-menu>

View file

@ -0,0 +1,74 @@
<!--
~ Copyright 2021 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<kc-tabs-realm></kc-tabs-realm>
<ul class="nav nav-tabs nav-tabs-pf">
<li>
<a href="#/realms/{{realm.realm}}/client-policies/profiles">{{:: 'client-policies-profiles' | translate}}</a>
<kc-tooltip>{{:: 'client-policies-profiles.tooltip' | translate}}</kc-tooltip>
</li>
<li class="active">
<a href="#/realms/{{realm.realm}}/client-policies/policies">{{:: 'client-policies-policies' | translate}}</a>
<kc-tooltip>{{:: 'client-policies-policies.tooltip' | translate}}</kc-tooltip>
</li>
</ul>
<ul class="nav nav-tabs nav-tabs-pf">
<li class="active"><a href="#/realms/{{realm.realm}}/client-policies/policies">{{:: 'client-profiles-form-view' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/client-policies/policies-json">{{:: 'client-profiles-json-editor' | translate}}</a></li>
</ul>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th class="kc-table-actions" colspan="6">
<div class="form-inline">
<div class="pull-right" data-ng-show="access.manageRealm">
<a href="#/realms/{{realm.realm}}/client-policies/policy-create" class="btn btn-default">{{:: 'create' | translate}}</a>
</div>
</div>
</th>
</tr>
<tr>
<th>{{:: 'name' | translate}}</th>
<th>{{:: 'description' | translate}}</th>
<th>{{:: 'enabled' | translate}}</th>
<th colspan="2">{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="clientPolicy in clientPolicies.policies">
<td><a href="#/realms/{{realm.realm}}/client-policies/policies-update/{{clientPolicy.name}}">{{clientPolicy.name}}</a></td>
<td>{{clientPolicy.description}}</td>
<td translate="{{clientPolicy.enabled}}"></td>
<td class="kc-action-cell" data-ng-hide="clientPolicy.builtin" kc-open="/realms/{{realm.realm}}/client-policies/policies-update/{{clientPolicy.name}}">{{:: 'edit' | translate}}</td>
<td class="kc-action-cell" data-ng-hide="clientPolicy.builtin" data-ng-click="removeClientPolicy(clientPolicy)">{{:: 'delete' | translate}}</td>
</tr>
</tbody>
</table>
</fieldset>
</form>
</div>
<kc-menu></kc-menu>

View file

@ -0,0 +1,58 @@
<!--
~ Copyright 2021 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1 data-ng-hide="createNew">{{conditionType.id|capitalize}}</h1>
<h1 data-ng-show="createNew">{{:: 'create-condition' | translate}}</h1>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="readOnly">
<fieldset>
<div class="form-group" data-ng-show="createNew">
<label class="col-md-2 control-label" for="conditionTypeCreate">{{:: 'condition-type' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="conditionTypeCreate"
ng-model="conditionType"
ng-options="conditionType.id for (conditionKey, conditionType) in conditionTypes">
</select>
</div>
</div>
<kc-tooltip>{{conditionType.helpText}}</kc-tooltip>
</div>
<div class="form-group clearfix" data-ng-hide="createNew">
<label class="col-md-2 control-label" for="conditionType">{{:: 'condition-type' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="conditionType" type="text" ng-model="conditionType.id" data-ng-readonly="true">
</div>
<kc-tooltip>{{conditionType.helpText}}</kc-tooltip>
</div>
<kc-provider-config config="condition.config" properties="conditionType.properties" realm="realm"></kc-provider-config>
</fieldset>
<div class="form-group" data-ng-hide="readOnly">
<div class="col-md-10 col-md-offset-2">
<button kc-save>{{:: 'save' | translate}}</button>
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
</div>
</div>
</form>
</div>
<kc-menu></kc-menu>

View file

@ -0,0 +1,148 @@
<!--
~ Copyright 2021 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<kc-tabs-realm></kc-tabs-realm>
<ul class="nav nav-tabs nav-tabs-pf">
<li>
<a href="#/realms/{{realm.realm}}/client-policies/profiles">{{:: 'client-policies-profiles' | translate}}</a>
<kc-tooltip>{{:: 'client-policies-profiles.tooltip' | translate}}</kc-tooltip>
</li>
<li class="active">
<a href="#/realms/{{realm.realm}}/client-policies/policies">{{:: 'client-policies-policies' | translate}}</a>
<kc-tooltip>{{:: 'client-policies-policies.tooltip' | translate}}</kc-tooltip>
</li>
</ul>
<ul class="nav nav-tabs nav-tabs-pf">
<li class="active"><a href="#/realms/{{realm.realm}}/client-policies/policies">{{:: 'client-profiles-form-view' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/client-policies/policies-json">{{:: 'client-profiles-json-editor' | translate}}</a></li>
</ul>
<form class="form-horizontal" name="realmForm" novalidate>
<fieldset class="border-top" kc-read-only="readOnly">
<div class="form-group">
<label class="col-md-2 control-label" for="clientPolicyName">{{:: 'name' | translate}} <span class="required">*</span></label>
<div class="col-sm-6">
<input class="form-control" type="text" id="clientPolicyName" name="clientPolicyName" data-ng-model="editedPolicy.name" autofocus required>
</div>
<kc-tooltip>{{:: 'client-policy-name.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="description">{{:: 'description' | translate}} </label>
<div class="col-md-6">
<textarea class="form-control" rows="5" cols="50" id="description" name="description" data-ng-model="editedPolicy.description"></textarea>
</div>
</div>
<div class="form-group clearfix block">
<label class="col-md-2 control-label" for="enabled">{{:: 'enabled' | translate}}</label>
<div class="col-sm-6">
<input ng-model="editedPolicy.enabled" name="enabled" id="enabled" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
<kc-tooltip>{{:: 'client-policy-enabled.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
<fieldset data-ng-hide="readOnly">
<div class="form-group">
<div class="col-md-10 col-md-offset-2">
<button kc-save>{{:: 'save' | translate}}</button>
<button kc-cancel data-ng-click="back()">{{:: 'back' | translate}}</button>
</div>
</div>
</fieldset>
<fieldset data-ng-hide="createNew">
<legend><span class="text">{{:: 'conditions' | translate}}</span> <kc-tooltip>{{:: 'client-policy-conditions.tooltip' | translate}}</kc-tooltip></legend>
<table class="table table-striped table-bordered">
<thead>
<tr data-ng-hide="readOnly">
<th class="kc-table-actions" colspan="3">
<div class="form-inline">
<div class="pull-right">
<a href="#/realms/{{realm.realm}}/client-policies/policies-update/{{editedPolicy.name}}/create-condition" class="btn btn-default">{{:: 'create' | translate}}</a>
</div>
</div>
</th>
</tr>
<tr data-ng-show="editedPolicy.conditions && editedPolicy.conditions.length > 0">
<th>{{:: 'type' | translate}}</th>
<th colspan="2">{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="condition in editedPolicy.conditions">
<td><a href="#/realms/{{realm.realm}}/client-policies/policies-update/{{editedPolicy.name}}/update-condition/{{$index}}">{{condition.condition}}</a></td>
<td class="kc-action-cell" data-ng-hide="readOnly" kc-open="/realms/{{realm.realm}}/client-policies/policies-update/{{editedPolicy.name}}/update-condition/{{$index}}">{{:: 'edit' | translate}}</td>
<td class="kc-action-cell" data-ng-hide="readOnly" data-ng-click="removeCondition($index)">{{:: 'delete' | translate}}</td>
</tr>
<tr data-ng-show="!editedPolicy.conditions || editedPolicy.conditions.length == 0">
<td>{{:: 'no-conditions-available' | translate}}</td>
</tr>
</tbody>
</table>
</fieldset>
<fieldset data-ng-hide="createNew">
<legend><span class="text">{{:: 'client-profiles' | translate}}</span></legend><kc-tooltip>{{:: 'client-profiles.tooltip' | translate}}</kc-tooltip>
<table class="table table-striped table-bordered">
<thead>
<tr data-ng-hide="readOnly || availableProfiles.length == 0">
<th colspan="5" class="kc-table-actions">
<div class="pull-right">
<div>
<select class="form-control" ng-model="selectedProfile"
ng-options="p for p in availableProfiles"
data-ng-change="addProfile(selectedProfile); selectedProfile = null">
<option value="" disabled selected>{{:: 'add-profile.placeholder' | translate}}</option>
</select>
</div>
</div>
</th>
</tr>
<tr data-ng-show="editedPolicy.profiles && editedPolicy.profiles.length > 0">
<th>{{:: 'name' | translate}}</th>
<th>{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="profileName in editedPolicy.profiles">
<td><a href="#/realms/{{realm.realm}}/client-policies/profiles-update/{{profileName}}">{{profileName}}</a></td>
<td class="kc-action-cell" data-ng-hide="readOnly" data-ng-click="removeProfile(profileName)">{{:: 'delete' | translate}}</td>
</tr>
<tr data-ng-show="!editedPolicy.profiles || editedPolicy.profiles.length == 0">
<td class="text-muted">{{:: 'no-client-profiles-configured' | translate}}</td>
</tr>
</tbody>
</table>
</fieldset>
</form>
</div>
<kc-menu></kc-menu>

View file

@ -0,0 +1,58 @@
<!--
~ Copyright 2021 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1 data-ng-hide="createNew">{{executorType.id|capitalize}}</h1>
<h1 data-ng-show="createNew">{{:: 'create-executor' | translate}}</h1>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="readOnly">
<fieldset>
<div class="form-group" data-ng-show="createNew">
<label class="col-md-2 control-label" for="executorTypeCreate">{{:: 'executor-type' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="executorTypeCreate"
ng-model="executorType"
ng-options="executorType.id for (executorKey, executorType) in executorTypes">
</select>
</div>
</div>
<kc-tooltip>{{executorType.helpText}}</kc-tooltip>
</div>
<div class="form-group clearfix" data-ng-hide="createNew">
<label class="col-md-2 control-label" for="executorType">{{:: 'executor-type' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="executorType" type="text" ng-model="executorType.id" data-ng-readonly="true">
</div>
<kc-tooltip>{{executorType.helpText}}</kc-tooltip>
</div>
<kc-provider-config config="executor.config" properties="executorType.properties" realm="realm"></kc-provider-config>
</fieldset>
<div class="form-group" data-ng-hide="readOnly">
<div class="col-md-10 col-md-offset-2">
<button kc-save>{{:: 'save' | translate}}</button>
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
</div>
</div>
</form>
</div>
<kc-menu></kc-menu>

View file

@ -0,0 +1,105 @@
<!--
~ Copyright 2021 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<kc-tabs-realm></kc-tabs-realm>
<ul class="nav nav-tabs nav-tabs-pf">
<li class="active">
<a href="#/realms/{{realm.realm}}/client-policies/profiles">{{:: 'client-policies-profiles' | translate}}</a>
<kc-tooltip>{{:: 'client-policies-profiles.tooltip' | translate}}</kc-tooltip>
</li>
<li>
<a href="#/realms/{{realm.realm}}/client-policies/policies">{{:: 'client-policies-policies' | translate}}</a>
<kc-tooltip>{{:: 'client-policies-policies.tooltip' | translate}}</kc-tooltip>
</li>
</ul>
<ul class="nav nav-tabs nav-tabs-pf">
<li class="active"><a href="#/realms/{{realm.realm}}/client-policies/profiles">{{:: 'client-profiles-form-view' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/client-policies/profiles-json">{{:: 'client-profiles-json-editor' | translate}}</a></li>
</ul>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="readOnly">
<fieldset class="border-top">
<div class="form-group">
<label class="col-md-2 control-label" for="clientProfileName">{{:: 'name' | translate}} <span class="required">*</span></label>
<div class="col-sm-6">
<input class="form-control" type="text" id="clientProfileName" name="clientProfileName" data-ng-model="editedProfile.name" autofocus required>
</div>
<kc-tooltip>{{:: 'client-profile-name.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="description">{{:: 'description' | translate}} </label>
<div class="col-md-6">
<textarea class="form-control" rows="5" cols="50" id="description" name="description" data-ng-model="editedProfile.description"></textarea>
</div>
</div>
</fieldset>
<fieldset>
<div class="form-group">
<div class="col-md-10 col-md-offset-2">
<button kc-save>{{:: 'save' | translate}}</button>
<button kc-cancel data-ng-click="back()">{{:: 'back' | translate}}</button>
</div>
</div>
</fieldset>
<fieldset data-ng-hide="createNew">
<legend><span class="text">{{:: 'executors' | translate}}</span> <kc-tooltip>{{:: 'client-profile-executors.tooltip' | translate}}</kc-tooltip></legend>
<table class="table table-striped table-bordered">
<thead>
<tr data-ng-hide="readOnly">
<th class="kc-table-actions" colspan="3">
<div class="form-inline">
<div class="pull-right">
<a href="#/realms/{{realm.realm}}/client-policies/profiles-update/{{editedProfile.name}}/create-executor" class="btn btn-default">{{:: 'create' | translate}}</a>
</div>
</div>
</th>
</tr>
<tr data-ng-show="editedProfile.executors && editedProfile.executors.length > 0">
<th>{{:: 'type' | translate}}</th>
<th colspan="2">{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="executor in editedProfile.executors">
<td><a href="#/realms/{{realm.realm}}/client-policies/profiles-update/{{editedProfile.name}}/update-executor/{{$index}}">{{executor.executor}}</a></td>
<td class="kc-action-cell" data-ng-hide="readOnly" kc-open="/realms/{{realm.realm}}/client-policies/profiles-update/{{editedProfile.name}}/update-executor/{{$index}}">{{:: 'edit' | translate}}</td>
<td class="kc-action-cell" data-ng-hide="readOnly" data-ng-click="removeExecutor($index)">{{:: 'delete' | translate}}</td>
</tr>
<tr data-ng-show="!editedProfile.executors || editedProfile.executors.length == 0">
<td>{{:: 'no-executors-available' | translate}}</td>
</tr>
</tbody>
</table>
</fieldset>
</form>
</div>
<kc-menu></kc-menu>

View file

@ -0,0 +1,60 @@
<!--
~ Copyright 2021 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<kc-tabs-realm></kc-tabs-realm>
<ul class="nav nav-tabs nav-tabs-pf">
<li class="active">
<a href="#/realms/{{realm.realm}}/client-policies/profiles">{{:: 'client-policies-profiles' | translate}}</a>
<kc-tooltip>{{:: 'client-policies-profiles.tooltip' | translate}}</kc-tooltip>
</li>
<li>
<a href="#/realms/{{realm.realm}}/client-policies/policies">{{:: 'client-policies-policies' | translate}}</a>
<kc-tooltip>{{:: 'client-policies-policies.tooltip' | translate}}</kc-tooltip>
</li>
</ul>
<ul class="nav nav-tabs nav-tabs-pf">
<li><a href="#/realms/{{realm.realm}}/client-policies/profiles">{{:: 'client-profiles-form-view' | translate}}</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/client-policies/profiles-json">{{:: 'client-profiles-json-editor' | translate}}</a></li>
</ul>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<filedset>
<div class="form-group">
<div class="col-md-10">
<div>
<textarea id="clientProfilesConfig" name="clientProfilesConfig" data-ng-model="clientProfilesString" class="form-control ng-binding" rows="20" ></textarea>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<button class="btn btn-primary" data-ng-click="save()">{{:: 'save' | translate}}</button>
<button class="btn btn-default" data-ng-click="reset()">{{:: 'reset' | translate}}</button>
</div>
</div>
</filedset>
</form>
</div>
<kc-menu></kc-menu>

View file

@ -0,0 +1,81 @@
<!--
~ Copyright 2021 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<kc-tabs-realm></kc-tabs-realm>
<ul class="nav nav-tabs nav-tabs-pf">
<li class="active">
<a href="#/realms/{{realm.realm}}/client-policies/profiles">{{:: 'client-policies-profiles' | translate}}</a>
<kc-tooltip>{{:: 'client-policies-profiles.tooltip' | translate}}</kc-tooltip>
</li>
<li>
<a href="#/realms/{{realm.realm}}/client-policies/policies">{{:: 'client-policies-policies' | translate}}</a>
<kc-tooltip>{{:: 'client-policies-policies.tooltip' | translate}}</kc-tooltip>
</li>
</ul>
<ul class="nav nav-tabs nav-tabs-pf">
<li class="active"><a href="#/realms/{{realm.realm}}/client-policies/profiles">{{:: 'client-profiles-form-view' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/client-policies/profiles-json">{{:: 'client-profiles-json-editor' | translate}}</a></li>
</ul>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th class="kc-table-actions" colspan="6">
<div class="form-inline">
<div class="pull-right" data-ng-show="access.manageRealm">
<a href="#/realms/{{realm.realm}}/client-policies/profiles-create" class="btn btn-default">{{:: 'create' | translate}}</a>
</div>
</div>
</th>
</tr>
<tr>
<th>{{:: 'name' | translate}}</th>
<th>{{:: 'description' | translate}}</th>
<th>{{:: 'global' | translate}}</th>
<th colspan="2">{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="clientProfile in clientProfiles.globalProfiles">
<td><a href="#/realms/{{realm.realm}}/client-policies/profiles-update/{{clientProfile.name}}">{{clientProfile.name}}</a></td>
<td>{{clientProfile.description}}</td>
<td>{{:: 'true' | translate}}</td>
<td></td>
<td></td>
</tr>
<tr ng-repeat="clientProfile in clientProfiles.profiles">
<td><a href="#/realms/{{realm.realm}}/client-policies/profiles-update/{{clientProfile.name}}">{{clientProfile.name}}</a></td>
<td>{{clientProfile.description}}</td>
<td>{{:: 'false' | translate}}</td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/client-policies/profiles-update/{{clientProfile.name}}">{{:: 'edit' | translate}}</td>
<td class="kc-action-cell" data-ng-click="removeClientProfile(clientProfile)">{{:: 'delete' | translate}}</td>
</tr>
</tbody>
</table>
</fieldset>
</form>
</div>
<kc-menu></kc-menu>

View file

@ -26,6 +26,7 @@
|| path[2] == 'theme-settings' || path[2] == 'theme-settings'
|| path[2] == 'localization' || path[2] == 'localization'
|| path[2] == 'token-settings' || path[2] == 'token-settings'
|| path[2] == 'client-policies'
|| path[2] == 'client-registration' || path[2] == 'client-registration'
|| path[2] == 'cache-settings' || path[2] == 'cache-settings'
|| path[2] == 'client-initial-access' || path[2] == 'client-initial-access'

View file

@ -16,6 +16,9 @@
<option value="" selected> {{:: 'selectOne' | translate}} </option> <option value="" selected> {{:: 'selectOne' | translate}} </option>
</select> </select>
</div> </div>
<div class="col-md-6" data-ng-if="option.type == 'MultivaluedList'">
<input ui-select2="option.mvOptions" ng-model="config[ option.name ]" data-placeholder="{{:: 'selectMultiple' | translate}}..."/>
</div>
<div class="col-md-6" data-ng-if="option.type == 'Role'"> <div class="col-md-6" data-ng-if="option.type == 'Role'">
<div class="row"> <div class="row">
<div class="col-md-8"> <div class="col-md-8">

View file

@ -15,6 +15,9 @@
<li ng-class="{active: path[2] == 'cache-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/cache-settings">{{:: 'realm-tab-cache' | translate}}</a></li> <li ng-class="{active: path[2] == 'cache-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/cache-settings">{{:: 'realm-tab-cache' | translate}}</a></li>
<li ng-class="{active: path[2] == 'token-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/token-settings">{{:: 'realm-tab-tokens' | translate}}</a></li> <li ng-class="{active: path[2] == 'token-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/token-settings">{{:: 'realm-tab-tokens' | translate}}</a></li>
<li ng-class="{active: path[2] == 'client-registration'}" data-ng-show="access.viewClients"><a href="#/realms/{{realm.realm}}/client-registration/client-initial-access">{{:: 'realm-tab-client-registration' | translate}}</a></li> <li ng-class="{active: path[2] == 'client-registration'}" data-ng-show="access.viewClients"><a href="#/realms/{{realm.realm}}/client-registration/client-initial-access">{{:: 'realm-tab-client-registration' | translate}}</a></li>
<li ng-class="{active: path[2] == 'client-policies'}" data-ng-show="access.viewRealm && serverInfo.featureEnabled('CLIENT_POLICIES')">
<a href="#/realms/{{realm.realm}}/client-policies/profiles">{{:: 'realm-tab-client-policies' | translate}}</a>
</li>
<li ng-class="{active: path[2] == 'defense'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/defense/headers">{{:: 'realm-tab-security-defenses' | translate}}</a></li> <li ng-class="{active: path[2] == 'defense'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/defense/headers">{{:: 'realm-tab-security-defenses' | translate}}</a></li>
</ul> </ul>
</div> </div>