KEYCLOAK-2594
bind credential being leaked in admin tool JSON response KEYCLOAK-2972 Keycloak leaks configuration passwords in Admin Event logs
This commit is contained in:
parent
1bf24d26a4
commit
5a00aaefa8
14 changed files with 231 additions and 40 deletions
|
@ -30,6 +30,13 @@ import java.util.Map;
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class MultivaluedHashMap<K, V> extends HashMap<K, List<V>>
|
public class MultivaluedHashMap<K, V> extends HashMap<K, List<V>>
|
||||||
{
|
{
|
||||||
|
public MultivaluedHashMap() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultivaluedHashMap(MultivaluedHashMap<K, V> config) {
|
||||||
|
addAll(config);
|
||||||
|
}
|
||||||
|
|
||||||
public void putSingle(K key, V value)
|
public void putSingle(K key, V value)
|
||||||
{
|
{
|
||||||
List<V> list = new ArrayList<V>();
|
List<V> list = new ArrayList<V>();
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -33,9 +34,25 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class ComponentUtil {
|
public class ComponentUtil {
|
||||||
|
|
||||||
|
public static Map<String, ProviderConfigProperty> getComponentConfigProperties(KeycloakSession session, ComponentRepresentation component) {
|
||||||
|
return getComponentConfigProperties(session, component.getProviderType(), component.getProviderId());
|
||||||
|
}
|
||||||
|
|
||||||
public static Map<String, ProviderConfigProperty> getComponentConfigProperties(KeycloakSession session, ComponentModel component) {
|
public static Map<String, ProviderConfigProperty> getComponentConfigProperties(KeycloakSession session, ComponentModel component) {
|
||||||
|
return getComponentConfigProperties(session, component.getProviderType(), component.getProviderId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ComponentFactory getComponentFactory(KeycloakSession session, ComponentRepresentation component) {
|
||||||
|
return getComponentFactory(session, component.getProviderType(), component.getProviderId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ComponentFactory getComponentFactory(KeycloakSession session, ComponentModel component) {
|
||||||
|
return getComponentFactory(session, component.getProviderType(), component.getProviderId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, ProviderConfigProperty> getComponentConfigProperties(KeycloakSession session, String providerType, String providerId) {
|
||||||
try {
|
try {
|
||||||
List<ProviderConfigProperty> l = getComponentFactory(session, component).getConfigProperties();
|
List<ProviderConfigProperty> l = getComponentFactory(session, providerType, providerId).getConfigProperties();
|
||||||
Map<String, ProviderConfigProperty> properties = new HashMap<>();
|
Map<String, ProviderConfigProperty> properties = new HashMap<>();
|
||||||
for (ProviderConfigProperty p : l) {
|
for (ProviderConfigProperty p : l) {
|
||||||
properties.put(p.getName(), p);
|
properties.put(p.getName(), p);
|
||||||
|
@ -46,15 +63,15 @@ public class ComponentUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ComponentFactory getComponentFactory(KeycloakSession session, ComponentModel component) {
|
private static ComponentFactory getComponentFactory(KeycloakSession session, String providerType, String providerId) {
|
||||||
Class<? extends Provider> provider = session.getProviderClass(component.getProviderType());
|
Class<? extends Provider> provider = session.getProviderClass(providerType);
|
||||||
if (provider == null) {
|
if (provider == null) {
|
||||||
throw new RuntimeException("Invalid provider type '" + component.getProviderType() + "'");
|
throw new RuntimeException("Invalid provider type '" + providerType + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
ProviderFactory<? extends Provider> f = session.getKeycloakSessionFactory().getProviderFactory(provider, component.getProviderId());
|
ProviderFactory<? extends Provider> f = session.getKeycloakSessionFactory().getProviderFactory(provider, providerId);
|
||||||
if (f == null) {
|
if (f == null) {
|
||||||
throw new RuntimeException("No such provider '" + component.getProviderId() + "'");
|
throw new RuntimeException("No such provider '" + providerId + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
ComponentFactory cf = (ComponentFactory) f;
|
ComponentFactory cf = (ComponentFactory) f;
|
||||||
|
|
|
@ -303,7 +303,7 @@ public class ModelToRepresentation {
|
||||||
rep.setAccessCodeLifespan(realm.getAccessCodeLifespan());
|
rep.setAccessCodeLifespan(realm.getAccessCodeLifespan());
|
||||||
rep.setAccessCodeLifespanUserAction(realm.getAccessCodeLifespanUserAction());
|
rep.setAccessCodeLifespanUserAction(realm.getAccessCodeLifespanUserAction());
|
||||||
rep.setAccessCodeLifespanLogin(realm.getAccessCodeLifespanLogin());
|
rep.setAccessCodeLifespanLogin(realm.getAccessCodeLifespanLogin());
|
||||||
rep.setSmtpServer(realm.getSmtpConfig());
|
rep.setSmtpServer(new HashMap<>(realm.getSmtpConfig()));
|
||||||
rep.setBrowserSecurityHeaders(realm.getBrowserSecurityHeaders());
|
rep.setBrowserSecurityHeaders(realm.getBrowserSecurityHeaders());
|
||||||
rep.setAccountTheme(realm.getAccountTheme());
|
rep.setAccountTheme(realm.getAccountTheme());
|
||||||
rep.setLoginTheme(realm.getLoginTheme());
|
rep.setLoginTheme(realm.getLoginTheme());
|
||||||
|
@ -385,6 +385,10 @@ public class ModelToRepresentation {
|
||||||
Map<String, String> attributes = realm.getAttributes();
|
Map<String, String> attributes = realm.getAttributes();
|
||||||
rep.setAttributes(attributes);
|
rep.setAttributes(attributes);
|
||||||
|
|
||||||
|
if (!internal) {
|
||||||
|
rep = StripSecretsUtils.strip(rep);
|
||||||
|
}
|
||||||
|
|
||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,7 +626,7 @@ public class ModelToRepresentation {
|
||||||
providerRep.setStoreToken(identityProviderModel.isStoreToken());
|
providerRep.setStoreToken(identityProviderModel.isStoreToken());
|
||||||
providerRep.setTrustEmail(identityProviderModel.isTrustEmail());
|
providerRep.setTrustEmail(identityProviderModel.isTrustEmail());
|
||||||
providerRep.setAuthenticateByDefault(identityProviderModel.isAuthenticateByDefault());
|
providerRep.setAuthenticateByDefault(identityProviderModel.isAuthenticateByDefault());
|
||||||
providerRep.setConfig(identityProviderModel.getConfig());
|
providerRep.setConfig(new HashMap<>(identityProviderModel.getConfig()));
|
||||||
providerRep.setAddReadTokenRoleOnCreate(identityProviderModel.isAddReadTokenRoleOnCreate());
|
providerRep.setAddReadTokenRoleOnCreate(identityProviderModel.isAddReadTokenRoleOnCreate());
|
||||||
|
|
||||||
String firstBrokerLoginFlowId = identityProviderModel.getFirstBrokerLoginFlowId();
|
String firstBrokerLoginFlowId = identityProviderModel.getFirstBrokerLoginFlowId();
|
||||||
|
@ -796,24 +800,9 @@ public class ModelToRepresentation {
|
||||||
rep.setProviderType(component.getProviderType());
|
rep.setProviderType(component.getProviderType());
|
||||||
rep.setSubType(component.getSubType());
|
rep.setSubType(component.getSubType());
|
||||||
rep.setParentId(component.getParentId());
|
rep.setParentId(component.getParentId());
|
||||||
if (internal) {
|
rep.setConfig(new MultivaluedHashMap<>(component.getConfig()));
|
||||||
rep.setConfig(component.getConfig());
|
if (!internal) {
|
||||||
} else {
|
rep = StripSecretsUtils.strip(session, rep);
|
||||||
Map<String, ProviderConfigProperty> configProperties = ComponentUtil.getComponentConfigProperties(session, component);
|
|
||||||
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
|
|
||||||
|
|
||||||
for (Map.Entry<String, List<String>> e : component.getConfig().entrySet()) {
|
|
||||||
ProviderConfigProperty configProperty = configProperties.get(e.getKey());
|
|
||||||
if (configProperty != null) {
|
|
||||||
if (configProperty.isSecret()) {
|
|
||||||
config.putSingle(e.getKey(), ComponentRepresentation.SECRET_VALUE);
|
|
||||||
} else {
|
|
||||||
config.put(e.getKey(), e.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rep.setConfig(config);
|
|
||||||
}
|
}
|
||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
|
@ -815,7 +815,12 @@ public class RepresentationToModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rep.getSmtpServer() != null) {
|
if (rep.getSmtpServer() != null) {
|
||||||
realm.setSmtpConfig(new HashMap(rep.getSmtpServer()));
|
Map<String, String> config = new HashMap(rep.getSmtpServer());
|
||||||
|
if (rep.getSmtpServer().containsKey("password") && ComponentRepresentation.SECRET_VALUE.equals(rep.getSmtpServer().get("password"))) {
|
||||||
|
String passwordValue = realm.getSmtpConfig() != null ? realm.getSmtpConfig().get("password") : null;
|
||||||
|
config.put("password", passwordValue);
|
||||||
|
}
|
||||||
|
realm.setSmtpConfig(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rep.getBrowserSecurityHeaders() != null) {
|
if (rep.getBrowserSecurityHeaders() != null) {
|
||||||
|
@ -1543,7 +1548,7 @@ public class RepresentationToModel {
|
||||||
identityProviderModel.setAuthenticateByDefault(representation.isAuthenticateByDefault());
|
identityProviderModel.setAuthenticateByDefault(representation.isAuthenticateByDefault());
|
||||||
identityProviderModel.setStoreToken(representation.isStoreToken());
|
identityProviderModel.setStoreToken(representation.isStoreToken());
|
||||||
identityProviderModel.setAddReadTokenRoleOnCreate(representation.isAddReadTokenRoleOnCreate());
|
identityProviderModel.setAddReadTokenRoleOnCreate(representation.isAddReadTokenRoleOnCreate());
|
||||||
identityProviderModel.setConfig(representation.getConfig());
|
identityProviderModel.setConfig(new HashMap<>(representation.getConfig()));
|
||||||
|
|
||||||
String flowAlias = representation.getFirstBrokerLoginFlowAlias();
|
String flowAlias = representation.getFirstBrokerLoginFlowAlias();
|
||||||
if (flowAlias == null) {
|
if (flowAlias == null) {
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 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.models.utils;
|
||||||
|
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class StripSecretsUtils {
|
||||||
|
|
||||||
|
public static ComponentRepresentation strip(KeycloakSession session, ComponentRepresentation rep) {
|
||||||
|
Map<String, ProviderConfigProperty> configProperties = ComponentUtil.getComponentConfigProperties(session, rep);
|
||||||
|
Iterator<Map.Entry<String, List<String>>> itr = rep.getConfig().entrySet().iterator();
|
||||||
|
while (itr.hasNext()) {
|
||||||
|
Map.Entry<String, List<String>> next = itr.next();
|
||||||
|
ProviderConfigProperty configProperty = configProperties.get(next.getKey());
|
||||||
|
if (configProperty != null) {
|
||||||
|
if (configProperty.isSecret()) {
|
||||||
|
next.setValue(Collections.singletonList(ComponentRepresentation.SECRET_VALUE));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
itr.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RealmRepresentation strip(RealmRepresentation rep) {
|
||||||
|
if (rep.getSmtpServer() != null && rep.getSmtpServer().containsKey("password")) {
|
||||||
|
rep.getSmtpServer().put("password", ComponentRepresentation.SECRET_VALUE);
|
||||||
|
}
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IdentityProviderRepresentation strip(IdentityProviderRepresentation rep) {
|
||||||
|
if (rep.getConfig() != null && rep.getConfig().containsKey("clientSecret")) {
|
||||||
|
rep.getConfig().put("clientSecret", ComponentRepresentation.SECRET_VALUE);
|
||||||
|
}
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
import org.keycloak.models.utils.StripSecretsUtils;
|
||||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
import org.keycloak.services.ErrorResponse;
|
import org.keycloak.services.ErrorResponse;
|
||||||
import org.keycloak.services.ErrorResponseException;
|
import org.keycloak.services.ErrorResponseException;
|
||||||
|
@ -119,7 +120,7 @@ public class ComponentResource {
|
||||||
|
|
||||||
model = realm.addComponentModel(model);
|
model = realm.addComponentModel(model);
|
||||||
|
|
||||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, model.getId()).representation(rep).success();
|
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, model.getId()).representation(StripSecretsUtils.strip(session, rep)).success();
|
||||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
|
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
|
||||||
} catch (ComponentValidationException e) {
|
} catch (ComponentValidationException e) {
|
||||||
return localizedErrorResponse(e);
|
return localizedErrorResponse(e);
|
||||||
|
@ -149,7 +150,7 @@ public class ComponentResource {
|
||||||
throw new NotFoundException("Could not find component");
|
throw new NotFoundException("Could not find component");
|
||||||
}
|
}
|
||||||
RepresentationToModel.updateComponent(session, rep, model, false);
|
RepresentationToModel.updateComponent(session, rep, model, false);
|
||||||
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo, model.getId()).representation(rep).success();
|
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo, model.getId()).representation(StripSecretsUtils.strip(session, rep)).success();
|
||||||
realm.updateComponent(model);
|
realm.updateComponent(model);
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
} catch (ComponentValidationException e) {
|
} catch (ComponentValidationException e) {
|
||||||
|
|
|
@ -35,8 +35,10 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
import org.keycloak.models.utils.StripSecretsUtils;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
import org.keycloak.representations.idm.ConfigPropertyRepresentation;
|
import org.keycloak.representations.idm.ConfigPropertyRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation;
|
||||||
|
@ -101,7 +103,7 @@ public class IdentityProviderResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
IdentityProviderRepresentation rep = ModelToRepresentation.toRepresentation(realm, this.identityProviderModel);
|
IdentityProviderRepresentation rep = ModelToRepresentation.toRepresentation(realm, this.identityProviderModel);
|
||||||
return rep;
|
return StripSecretsUtils.strip(rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -152,12 +154,18 @@ public class IdentityProviderResource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void updateIdpFromRep(IdentityProviderRepresentation providerRep, RealmModel realm, KeycloakSession session) {
|
private void updateIdpFromRep(IdentityProviderRepresentation providerRep, RealmModel realm, KeycloakSession session) {
|
||||||
String internalId = providerRep.getInternalId();
|
String internalId = providerRep.getInternalId();
|
||||||
String newProviderId = providerRep.getAlias();
|
String newProviderId = providerRep.getAlias();
|
||||||
String oldProviderId = getProviderIdByInternalId(realm, internalId);
|
String oldProviderId = getProviderIdByInternalId(realm, internalId);
|
||||||
|
|
||||||
realm.updateIdentityProvider(RepresentationToModel.toModel(realm, providerRep));
|
IdentityProviderModel updated = RepresentationToModel.toModel(realm, providerRep);
|
||||||
|
|
||||||
|
if (updated.getConfig() != null && ComponentRepresentation.SECRET_VALUE.equals(updated.getConfig().get("clientSecret"))) {
|
||||||
|
updated.getConfig().put("clientSecret", identityProviderModel.getConfig() != null ? identityProviderModel.getConfig().get("clientSecret") : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
realm.updateIdentityProvider(updated);
|
||||||
|
|
||||||
if (oldProviderId != null && !oldProviderId.equals(newProviderId)) {
|
if (oldProviderId != null && !oldProviderId.equals(newProviderId)) {
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
import org.keycloak.models.utils.StripSecretsUtils;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
import org.keycloak.provider.ProviderFactory;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
import org.keycloak.services.ErrorResponse;
|
import org.keycloak.services.ErrorResponse;
|
||||||
|
@ -167,7 +168,7 @@ public class IdentityProvidersResource {
|
||||||
List<IdentityProviderRepresentation> representations = new ArrayList<IdentityProviderRepresentation>();
|
List<IdentityProviderRepresentation> representations = new ArrayList<IdentityProviderRepresentation>();
|
||||||
|
|
||||||
for (IdentityProviderModel identityProviderModel : realm.getIdentityProviders()) {
|
for (IdentityProviderModel identityProviderModel : realm.getIdentityProviders()) {
|
||||||
representations.add(ModelToRepresentation.toRepresentation(realm, identityProviderModel));
|
representations.add(StripSecretsUtils.strip(ModelToRepresentation.toRepresentation(realm, identityProviderModel)));
|
||||||
}
|
}
|
||||||
return representations;
|
return representations;
|
||||||
}
|
}
|
||||||
|
@ -191,7 +192,7 @@ public class IdentityProvidersResource {
|
||||||
|
|
||||||
representation.setInternalId(identityProvider.getInternalId());
|
representation.setInternalId(identityProvider.getInternalId());
|
||||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, identityProvider.getAlias())
|
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, identityProvider.getAlias())
|
||||||
.representation(representation).success();
|
.representation(StripSecretsUtils.strip(representation)).success();
|
||||||
|
|
||||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(representation.getAlias()).build()).build();
|
return Response.created(uriInfo.getAbsolutePathBuilder().path(representation.getAlias()).build()).build();
|
||||||
} catch (ModelDuplicateException e) {
|
} catch (ModelDuplicateException e) {
|
||||||
|
|
|
@ -49,6 +49,7 @@ import org.keycloak.models.cache.UserCache;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
import org.keycloak.models.utils.StripSecretsUtils;
|
||||||
import org.keycloak.partialimport.PartialImportManager;
|
import org.keycloak.partialimport.PartialImportManager;
|
||||||
import org.keycloak.protocol.oidc.TokenManager;
|
import org.keycloak.protocol.oidc.TokenManager;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
@ -309,7 +310,7 @@ public class RealmAdminResource {
|
||||||
usersSyncManager.notifyToRefreshPeriodicSync(session, realm, fedProvider, false);
|
usersSyncManager.notifyToRefreshPeriodicSync(session, realm, fedProvider, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
adminEvent.operation(OperationType.UPDATE).representation(rep).success();
|
adminEvent.operation(OperationType.UPDATE).representation(StripSecretsUtils.strip(rep)).success();
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
} catch (PatternSyntaxException e) {
|
} catch (PatternSyntaxException e) {
|
||||||
return ErrorResponse.error("Specified regex pattern(s) is invalid.", Response.Status.BAD_REQUEST);
|
return ErrorResponse.error("Specified regex pattern(s) is invalid.", Response.Status.BAD_REQUEST);
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.keycloak.keys.KeyProviderFactory;
|
||||||
import org.keycloak.models.AuthenticationFlowModel;
|
import org.keycloak.models.AuthenticationFlowModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RealmProvider;
|
import org.keycloak.models.RealmProvider;
|
||||||
|
@ -638,6 +639,20 @@ public class TestingResourceProvider implements RealmResourceProvider {
|
||||||
return reps;
|
return reps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/smtp-config")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Map<String, String> getSmtpConfig() {
|
||||||
|
return session.getContext().getRealm().getSmtpConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/identity-config")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Map<String, String> getIdentityProviderConfig(@QueryParam("alias") String alias) {
|
||||||
|
return session.getContext().getRealm().getIdentityProviderByAlias(alias).getConfig();
|
||||||
|
}
|
||||||
|
|
||||||
private RealmModel getRealmByName(String realmName) {
|
private RealmModel getRealmByName(String realmName) {
|
||||||
RealmProvider realmProvider = session.getProvider(RealmProvider.class);
|
RealmProvider realmProvider = session.getProvider(RealmProvider.class);
|
||||||
return realmProvider.getRealmByName(realmName);
|
return realmProvider.getRealmByName(realmName);
|
||||||
|
|
|
@ -242,4 +242,14 @@ public interface TestingResource {
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
Map<String, TestProvider.DetailsRepresentation> getTestComponentDetails();
|
Map<String, TestProvider.DetailsRepresentation> getTestComponentDetails();
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/smtp-config")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
Map<String, String> getSmtpConfig();
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/identity-config")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
Map<String, String> getIdentityProviderConfig(@QueryParam("alias") String alias);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.ComponentsResource;
|
import org.keycloak.admin.client.resource.ComponentsResource;
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
|
import org.keycloak.representations.idm.AdminEventRepresentation;
|
||||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||||
import org.keycloak.testsuite.components.TestProvider;
|
import org.keycloak.testsuite.components.TestProvider;
|
||||||
|
@ -155,6 +156,11 @@ public class ComponentsTest extends AbstractAdminTest {
|
||||||
ComponentRepresentation returned = components.component(id).toRepresentation();
|
ComponentRepresentation returned = components.component(id).toRepresentation();
|
||||||
assertEquals(ComponentRepresentation.SECRET_VALUE, returned.getConfig().getFirst("secret"));
|
assertEquals(ComponentRepresentation.SECRET_VALUE, returned.getConfig().getFirst("secret"));
|
||||||
|
|
||||||
|
// Check secret not leaked in admin events
|
||||||
|
AdminEventRepresentation event = testingClient.testing().pollAdminEvent();
|
||||||
|
assertFalse(event.getRepresentation().contains("some secret value!!"));
|
||||||
|
assertTrue(event.getRepresentation().contains(ComponentRepresentation.SECRET_VALUE));
|
||||||
|
|
||||||
Map<String, TestProvider.DetailsRepresentation> details = testingClient.testing(REALM_NAME).getTestComponentDetails();
|
Map<String, TestProvider.DetailsRepresentation> details = testingClient.testing(REALM_NAME).getTestComponentDetails();
|
||||||
|
|
||||||
// Check value is set correctly
|
// Check value is set correctly
|
||||||
|
@ -166,6 +172,11 @@ public class ComponentsTest extends AbstractAdminTest {
|
||||||
ComponentRepresentation returned2 = components.component(id).toRepresentation();
|
ComponentRepresentation returned2 = components.component(id).toRepresentation();
|
||||||
assertEquals(ComponentRepresentation.SECRET_VALUE, returned2.getConfig().getFirst("secret"));
|
assertEquals(ComponentRepresentation.SECRET_VALUE, returned2.getConfig().getFirst("secret"));
|
||||||
|
|
||||||
|
// Check secret not leaked in admin events
|
||||||
|
event = testingClient.testing().pollAdminEvent();
|
||||||
|
assertFalse(event.getRepresentation().contains("some secret value!!"));
|
||||||
|
assertTrue(event.getRepresentation().contains(ComponentRepresentation.SECRET_VALUE));
|
||||||
|
|
||||||
// Check secret value is not set to '*********'
|
// Check secret value is not set to '*********'
|
||||||
details = testingClient.testing(REALM_NAME).getTestComponentDetails();
|
details = testingClient.testing(REALM_NAME).getTestComponentDetails();
|
||||||
assertEquals("some secret value!!", details.get("mycomponent").getConfig().get("secret").get(0));
|
assertEquals("some secret value!!", details.get("mycomponent").getConfig().get("secret").get(0));
|
||||||
|
@ -176,6 +187,9 @@ public class ComponentsTest extends AbstractAdminTest {
|
||||||
// Check secret value is updated
|
// Check secret value is updated
|
||||||
details = testingClient.testing(REALM_NAME).getTestComponentDetails();
|
details = testingClient.testing(REALM_NAME).getTestComponentDetails();
|
||||||
assertEquals("updated secret value!!", details.get("mycomponent").getConfig().get("secret").get(0));
|
assertEquals("updated secret value!!", details.get("mycomponent").getConfig().get("secret").get(0));
|
||||||
|
|
||||||
|
ComponentRepresentation returned3 = components.query().stream().filter(c -> c.getId().equals(returned2.getId())).findFirst().get();
|
||||||
|
assertEquals(ComponentRepresentation.SECRET_VALUE, returned3.getConfig().getFirst("secret"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createComponent(ComponentRepresentation rep) {
|
private String createComponent(ComponentRepresentation rep) {
|
||||||
|
|
|
@ -27,6 +27,10 @@ import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
||||||
import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType;
|
import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType;
|
||||||
import org.keycloak.events.admin.OperationType;
|
import org.keycloak.events.admin.OperationType;
|
||||||
import org.keycloak.events.admin.ResourceType;
|
import org.keycloak.events.admin.ResourceType;
|
||||||
|
import org.keycloak.models.utils.StripSecretsUtils;
|
||||||
|
import org.keycloak.representations.idm.AdminEventRepresentation;
|
||||||
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
|
@ -78,7 +82,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
||||||
IdentityProviderRepresentation newIdentityProvider = createRep("new-identity-provider", "oidc");
|
IdentityProviderRepresentation newIdentityProvider = createRep("new-identity-provider", "oidc");
|
||||||
|
|
||||||
newIdentityProvider.getConfig().put("clientId", "clientId");
|
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||||
newIdentityProvider.getConfig().put("clientSecret", "clientSecret");
|
newIdentityProvider.getConfig().put("clientSecret", "some secret value");
|
||||||
|
|
||||||
create(newIdentityProvider);
|
create(newIdentityProvider);
|
||||||
|
|
||||||
|
@ -94,10 +98,17 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
||||||
assertEquals("new-identity-provider", representation.getAlias());
|
assertEquals("new-identity-provider", representation.getAlias());
|
||||||
assertEquals("oidc", representation.getProviderId());
|
assertEquals("oidc", representation.getProviderId());
|
||||||
assertEquals("clientId", representation.getConfig().get("clientId"));
|
assertEquals("clientId", representation.getConfig().get("clientId"));
|
||||||
assertEquals("clientSecret", representation.getConfig().get("clientSecret"));
|
assertEquals(ComponentRepresentation.SECRET_VALUE, representation.getConfig().get("clientSecret"));
|
||||||
assertTrue(representation.isEnabled());
|
assertTrue(representation.isEnabled());
|
||||||
assertFalse(representation.isStoreToken());
|
assertFalse(representation.isStoreToken());
|
||||||
assertFalse(representation.isTrustEmail());
|
assertFalse(representation.isTrustEmail());
|
||||||
|
|
||||||
|
testingClient.testing("admin-client-test").getSmtpConfig();
|
||||||
|
|
||||||
|
assertEquals("some secret value", testingClient.testing("admin-client-test").getIdentityProviderConfig("new-identity-provider").get("clientSecret"));
|
||||||
|
|
||||||
|
IdentityProviderRepresentation rep = realm.identityProviders().findAll().stream().filter(i -> i.getAlias().equals("new-identity-provider")).findFirst().get();
|
||||||
|
assertEquals(ComponentRepresentation.SECRET_VALUE, rep.getConfig().get("clientSecret"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -105,7 +116,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
||||||
IdentityProviderRepresentation newIdentityProvider = createRep("update-identity-provider", "oidc");
|
IdentityProviderRepresentation newIdentityProvider = createRep("update-identity-provider", "oidc");
|
||||||
|
|
||||||
newIdentityProvider.getConfig().put("clientId", "clientId");
|
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||||
newIdentityProvider.getConfig().put("clientSecret", "clientSecret");
|
newIdentityProvider.getConfig().put("clientSecret", "some secret value");
|
||||||
|
|
||||||
create(newIdentityProvider);
|
create(newIdentityProvider);
|
||||||
|
|
||||||
|
@ -125,7 +136,9 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
||||||
representation.getConfig().put("clientId", "changedClientId");
|
representation.getConfig().put("clientId", "changedClientId");
|
||||||
|
|
||||||
identityProviderResource.update(representation);
|
identityProviderResource.update(representation);
|
||||||
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.identityProviderPath("update-identity-provider"), representation, ResourceType.IDENTITY_PROVIDER);
|
AdminEventRepresentation event = assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.identityProviderPath("update-identity-provider"), representation, ResourceType.IDENTITY_PROVIDER);
|
||||||
|
assertFalse(event.getRepresentation().contains("some secret value"));
|
||||||
|
assertTrue(event.getRepresentation().contains(ComponentRepresentation.SECRET_VALUE));
|
||||||
|
|
||||||
identityProviderResource = realm.identityProviders().get(representation.getInternalId());
|
identityProviderResource = realm.identityProviders().get(representation.getInternalId());
|
||||||
|
|
||||||
|
@ -136,6 +149,8 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
||||||
assertFalse(representation.isEnabled());
|
assertFalse(representation.isEnabled());
|
||||||
assertTrue(representation.isStoreToken());
|
assertTrue(representation.isStoreToken());
|
||||||
assertEquals("changedClientId", representation.getConfig().get("clientId"));
|
assertEquals("changedClientId", representation.getConfig().get("clientId"));
|
||||||
|
|
||||||
|
assertEquals("some secret value", testingClient.testing("admin-client-test").getIdentityProviderConfig("changed-alias").get("clientSecret"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -168,7 +183,14 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
||||||
Assert.assertNotNull(ApiUtil.getCreatedId(response));
|
Assert.assertNotNull(ApiUtil.getCreatedId(response));
|
||||||
response.close();
|
response.close();
|
||||||
|
|
||||||
|
String secret = idpRep.getConfig() != null ? idpRep.getConfig().get("clientSecret") : null;
|
||||||
|
idpRep = StripSecretsUtils.strip(idpRep);
|
||||||
|
|
||||||
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.identityProviderPath(idpRep.getAlias()), idpRep, ResourceType.IDENTITY_PROVIDER);
|
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.identityProviderPath(idpRep.getAlias()), idpRep, ResourceType.IDENTITY_PROVIDER);
|
||||||
|
|
||||||
|
if (secret != null) {
|
||||||
|
idpRep.getConfig().put("clientSecret", secret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IdentityProviderRepresentation createRep(String id, String providerId) {
|
private IdentityProviderRepresentation createRep(String id, String providerId) {
|
||||||
|
|
|
@ -31,7 +31,10 @@ import org.keycloak.events.admin.ResourceType;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.representations.adapters.action.GlobalRequestResult;
|
import org.keycloak.representations.adapters.action.GlobalRequestResult;
|
||||||
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
|
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
|
||||||
|
import org.keycloak.representations.idm.AdminEventRepresentation;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.representations.idm.EventRepresentation;
|
import org.keycloak.representations.idm.EventRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
|
@ -45,6 +48,7 @@ import org.keycloak.testsuite.auth.page.AuthRealm;
|
||||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||||
import org.keycloak.testsuite.util.CredentialBuilder;
|
import org.keycloak.testsuite.util.CredentialBuilder;
|
||||||
import org.keycloak.testsuite.util.OAuthClient.AccessTokenResponse;
|
import org.keycloak.testsuite.util.OAuthClient.AccessTokenResponse;
|
||||||
|
import org.keycloak.testsuite.util.RealmBuilder;
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
@ -124,6 +128,34 @@ public class RealmTest extends AbstractAdminTest {
|
||||||
Assert.assertNames(adminClient.realms().findAll(), "master", AuthRealm.TEST, REALM_NAME);
|
Assert.assertNames(adminClient.realms().findAll(), "master", AuthRealm.TEST, REALM_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void smtpPasswordSecret() {
|
||||||
|
RealmRepresentation rep = RealmBuilder.create().testEventListener().testMail().build();
|
||||||
|
rep.setRealm("realm-with-smtp");
|
||||||
|
rep.getSmtpServer().put("user", "user");
|
||||||
|
rep.getSmtpServer().put("password", "secret");
|
||||||
|
|
||||||
|
adminClient.realms().create(rep);
|
||||||
|
|
||||||
|
RealmRepresentation returned = adminClient.realm("realm-with-smtp").toRepresentation();
|
||||||
|
assertEquals(ComponentRepresentation.SECRET_VALUE, returned.getSmtpServer().get("password"));
|
||||||
|
|
||||||
|
assertEquals("secret", testingClient.testing("realm-with-smtp").getSmtpConfig().get("password"));
|
||||||
|
|
||||||
|
adminClient.realm("realm-with-smtp").update(rep);
|
||||||
|
|
||||||
|
AdminEventRepresentation event = testingClient.testing().pollAdminEvent();
|
||||||
|
assertFalse(event.getRepresentation().contains("some secret value!!"));
|
||||||
|
assertTrue(event.getRepresentation().contains(ComponentRepresentation.SECRET_VALUE));
|
||||||
|
|
||||||
|
assertEquals("secret", testingClient.testing("realm-with-smtp").getSmtpConfig().get("password"));
|
||||||
|
|
||||||
|
RealmRepresentation realm = adminClient.realms().findAll().stream().filter(r -> r.getRealm().equals("realm-with-smtp")).findFirst().get();
|
||||||
|
assertEquals(ComponentRepresentation.SECRET_VALUE, realm.getSmtpServer().get("password"));
|
||||||
|
|
||||||
|
adminClient.realm("realm-with-smtp").remove();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createRealmCheckDefaultPasswordPolicy() {
|
public void createRealmCheckDefaultPasswordPolicy() {
|
||||||
RealmRepresentation rep = new RealmRepresentation();
|
RealmRepresentation rep = new RealmRepresentation();
|
||||||
|
|
Loading…
Reference in a new issue