8000 Mask secrets in client installations when view-only permissions are used by vmuzikar · Pull Request #53758 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,24 @@ public interface ClientAuthenticatorFactory extends ProviderFactory<ClientAuthen
/**
* Get configuration, which needs to be used for adapter ( keycloak.json ) of particular client. Some implementations
* may return just template and user needs to edit the values according to his environment (For example fill the location of keystore file)
* Sensitive entries must be declared by {@link #getAdapterConfigurationSecretKeys()}.
*
* @return
*/
Map<String, Object> getAdapterConfiguration(KeycloakSession session, ClientModel client);

/**
* Declare all sensitive entries in the map returned by {@link #getAdapterConfiguration(KeycloakSession, ClientModel)}.
* Paths are slash-separated map keys with a leading slash, for example {@code /secret} or {@code /secret-jwt/secret}.
* Keys must be non-empty and cannot contain slashes. No escaping is supported.
* Only nested maps can be traversed; array traversal is not supported.
* Missing paths are permitted for optional configuration entries. An entry declared sensitive is masked in its
* entirety, including when its value is a map or collection. Non-sensitive template placeholders need not be declared.
*
* @return a non-null set of secret paths, or an empty set if the configuration contains no secrets
*/
Set<String> getAdapterConfigurationSecretKeys();
Comment thread
vmuzikar marked this conversation as resolved.
Resolved

/**
* Get authentication methods for the specified protocol
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@
* @version $Revision: 1 $
*/
public interface ClientInstallationProvider extends Provider, ProviderFactory<ClientInstallationProvider> {
Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI serverBaseUri);
/**
* Generates installation configuration, masking stored secrets when {@code includeSecrets} is false.
*/
Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI serverBaseUri, boolean includeSecrets);

String getProtocol();
String getDisplayType();
String getHelpText();
Expand Down
@@ -188,6 +188,11 @@ public Map<String, Object> getAdapterConfiguration(KeycloakSession session, Clie
Original file line number Diff line number Diff line change
Expand Up
return Map.of();
}

@Override
public Set<String> getAdapterConfigurationSecretKeys() {
return Set.of();
}

@Override
public boolean isSupported(Config.Scope config) {
return Profile.isFeatureEnabled(Profile.Feature.CLIENT_AUTH_ABCA);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ public Map<String, Object> getAdapterConfiguration(KeycloakSession session, Clie
return result;
}

@Override
public Set<String> getAdapterConfigurationSecretKeys() {
return Set.of("/secret");
}

@Override
public String getId() {
return PROVIDER_ID;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ public Map<String, Object> getAdapterConfiguration(KeycloakSession session, Clie
return Collections.emptyMap();
}

@Override
public Set<String> getAdapterConfigurationSecretKeys() {
return Set.of();
}

@Override
public Set<String> getProtocolAuthenticatorMethods(String loginProtocol) {
return Collections.emptySet();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,11 @@ public Map<String, Object> getAdapterConfiguration(KeycloakSession session, Clie
return config;
}

@Override
public Set<String> getAdapterConfigurationSecretKeys() {
return Set.of();
}

@Override
public String getId() {
return PROVIDER_ID;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ public Map<String, Object> getAdapterConfiguration(KeycloakSession session, Clie
return config;
}

@Override
public Set<String> getAdapterConfigurationSecretKeys() {
return Set.of("/secret-jwt/secret");
}

@Override
public Set<String> getProtocolAuthenticatorMethods(String loginProtocol) {
if (loginProtocol.equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ public Map<String, Object> getAdapterConfiguration(KeycloakSession session, Clie
return Collections.emptyMap();
}

@Override
public Set<String> getAdapterConfigurationSecretKeys() {
return Set.of();
}

@Override
public Set<String> getProtocolAuthenticatorMethods(String loginProtocol) {
if (loginProtocol.equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public String getId() {
}

@Override
public Response generateInstallation(final KeycloakSession session, final RealmModel realm, final ClientModel client, final URI serverBaseUri) {
public Response generateInstallation(final KeycloakSession session, final RealmModel realm, final ClientModel client, final URI serverBaseUri, boolean includeSecrets) {
final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
final ZipOutputStream zipOutput = new ZipOutputStream(byteStream);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public String getId() {
}

@Override
public Response generateInstallation(final KeycloakSession session, final RealmModel realm, final ClientModel client, final URI serverBaseUri) {
public Response generateInstallation(final KeycloakSession session, final RealmModel realm, final ClientModel client, final URI serverBaseUri, boolean includeSecrets) {
final StringBuilder responseString = new StringBuilder("auth:\n")
.append(" token:\n")
.append(" realm: ").append(serverBaseUri).append("/realms/").append(realm.getName()).append("/protocol/").append(DockerAuthV2Protocol.LOGIN_PROTOCOL).append("/auth\n")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public String getId() {

// TODO "auth" is not guaranteed to be the endpoint, fix it
@Override
public Response generateInstallation(final KeycloakSession session, final RealmModel realm, final ClientModel client, final URI serverBaseUri) {
public Response generateInstallation(final KeycloakSession session, final RealmModel realm, final ClientModel client, final URI serverBaseUri, boolean includeSecrets) {
final StringBuilder builder = new StringBuilder()
.append("-e REGISTRY_AUTH_TOKEN_REALM=").append(serverBaseUri).append("/realms/").append(realm.getName()).append("/protocol/").append(DockerAuthV2Protocol.LOGIN_PROTOCOL).append("/auth \\\n")
.append("-e REGISTRY_AUTH_TOKEN_SERVICE=").append(client.getClientId()).append(" \\\n")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@
import jakarta.ws.rs.core.Response;

import org.keycloak.Config;
import org.keycloak.authentication.ClientAuthenticator;
import org.keycloak.authentication.ClientAuthenticatorFactory;
import org.keycloak.authorization.admin.AuthorizationService;
import org.keycloak.common.Profile;
import org.keycloak.models.ClientModel;
Expand All @@ -41,6 +39,7 @@
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.mappers.AudienceProtocolMapper;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.services.managers.ClientInstallationUtils;
import org.keycloak.services.managers.ClientManager;
import org.keycloak.util.JsonSerialization;

Expand All @@ -51,7 +50,7 @@
public class KeycloakOIDCClientInstallation implements ClientInstallationProvider {

@Override
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, boolean includeSecrets) {
ClientManager.InstallationAdapterConfig rep = new ClientManager.InstallationAdapterConfig();
rep.setAuthServerUrl(baseUri.toString());
rep.setRealm(realm.getName());
Expand All @@ -64,7 +63,7 @@ public Response generateInstallation(KeycloakSession session, RealmModel realm,
rep.setResource(client.getClientId());

if (showClientCredentialsAdapterConfig(client)) {
Map<String, Object> adapterConfig = getClientCredentialsAdapterConfig(session, client);
Map<String, Object> adapterConfig = getClientCredentialsAdapterConfig(session, client, includeSecrets);
rep.setCredentials(adapterConfig);
}

Expand All @@ -83,10 +82,8 @@ public Response generateInstallation(KeycloakSession session, RealmModel realm,
return Response.ok(json, MediaType.TEXT_PLAIN_TYPE).build();
}

public static Map<String, Object> getClientCredentialsAdapterConfig(KeycloakSession session, ClientModel client) {
String clientAuthenticator = client.getClientAuthenticatorType();
ClientAuthenticatorFactory authenticator = (ClientAuthenticatorFactory) session.getKeycloakSessionFactory().getProviderFactory(ClientAuthenticator.class, clientAuthenticator);
return authenticator.getAdapterConfiguration(session, client);
public static Map<String, Object> getClientCredentialsAdapterConfig(KeycloakSession session, ClientModel client, boolean includeSecrets) {
return ClientInstallationUtils.getClientCredentialsAdapterConfig(session, client, includeSecrets);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
public class KeycloakOIDCJbossSubsystemClientCliInstallation implements ClientInstallationProvider {

@Override
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, boolean includeSecrets) {
String deploymentName = "WAR MODULE NAME.war";
StringBuilder builder = new StringBuilder();

Expand All @@ -62,7 +62,7 @@ public Response generateInstallation(KeycloakSession session, RealmModel realm,


if (KeycloakOIDCClientInstallation.showClientCredentialsAdapterConfig(client)) {
Map<String, Object> adapterConfig = KeycloakOIDCClientInstallation.getClientCredentialsAdapterConfig(session, client);
Map<String, Object> adapterConfig = KeycloakOIDCClientInstallation.getClientCredentialsAdapterConfig(session, client, includeSecrets);
for (Map.Entry<String, Object> entry : adapterConfig.entrySet()) {
builder.append("/subsystem=keycloak/secure-deployment=").append(quote(deploymentName)).append("/")
.append("credential=").append(entry.getKey()).append(":add(value=").append(entry.getValue())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
*/
public class KeycloakOIDCJbossSubsystemClientInstallation implements ClientInstallationProvider {
@Override
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, boolean includeSecrets) {
StringBuffer buffer = new StringBuffer();
buffer.append("<secure-deployment name=\"WAR MODULE NAME.war\">\n");
buffer.append(" <realm>").append(realm.getName()).append("</realm>\n");
Expand All @@ -56,7 +56,7 @@ public Response generateInstallation(KeycloakSession session, RealmModel realm,
}

if (KeycloakOIDCClientInstallation.showClientCredentialsAdapterConfig(client)) {
Map<String, Object> adapterConfig = KeycloakOIDCClientInstallation.getClientCredentialsAdapterConfig(session, client);
Map<String, Object> adapterConfig = KeycloakOIDCClientInstallation.getClientCredentialsAdapterConfig(session, client, includeSecrets);
for (Map.Entry<String, Object> entry : adapterConfig.entrySet()) {
buffer.append(" <credential name=\"" + entry.getKey() + "\">");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.keycloak.protocol.ClientInstallationProvider;
import org.keycloak.protocol.saml.SamlClient;
import org.keycloak.protocol.saml.SamlProtocol;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.services.resources.RealmsResource;

/**
Expand All @@ -40,16 +41,16 @@
public class KeycloakSamlClientInstallation implements ClientInstallationProvider {

@Override
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, boolean includeSecrets) {
SamlClient samlClient = new SamlClient(client);
StringBuilder buffer = new StringBuilder();
buffer.append("<keycloak-saml-adapter>\n");
baseXml(session, realm, client, baseUri, samlClient, buffer);
baseXml(session, realm, client, baseUri, samlClient, buffer, includeSecrets);
buffer.append("</keycloak-saml-adapter>\n");
return Response.ok(buffer.toString(), MediaType.TEXT_PLAIN_TYPE).build();
}

public static void baseXml(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, SamlClient samlClient, StringBuilder buffer) {
public static void baseXml(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, SamlClient samlClient, StringBuilder buffer, boolean includeSecrets) {
buffer.append(" <SP entityID=\"").append(client.getBaseUrl() == null ? "SPECIFY YOUR entityID!" : client.getBaseUrl()).append("\"\n");
buffer.append(" sslPolicy=\"").append(realm.getSslRequired().name()).append("\"\n");
buffer.append(" logoutPage=\"SPECIFY YOUR LOGOUT PAGE!\">\n");
Expand All @@ -61,7 +62,7 @@ public static void baseXml(KeycloakSession session, RealmModel realm, ClientMode
if (samlClient.getClientSigningPrivateKey() == null) {
buffer.append(" PRIVATE KEY NOT STORED ON SERVER - REPLACE WITH YOUR OWN PRIVATE KEY\n");
} else {
buffer.append(" ").append(samlClient.getClientSigningPrivateKey()).append("\n");
buffer.append(" ").append(includeSecrets ? samlClient.getClientSigningPrivateKey() : ComponentRepresentation.SECRET_VALUE).append("\n");
}
buffer.append(" </PrivateKeyPem>\n");
buffer.append(" <CertificatePem>\n");
Expand All @@ -79,7 +80,7 @@ public static void baseXml(KeycloakSession session, RealmModel realm, ClientMode
if (samlClient.getClientEncryptingPrivateKey() == null) {
buffer.append(" PRIVATE KEY NOT STORED ON SERVER - REPLACE WITH YOUR OWN PRIVATE KEY\n");
} else {
buffer.append(" ").append(samlClient.getClientEncryptingPrivateKey()).append("\n");
buffer.append(" ").append(includeSecrets ? samlClient.getClientEncryptingPrivateKey() : ComponentRepresentation.SECRET_VALUE).append("\n");
}
buffer.append(" </PrivateKeyPem>\n");
buffer.append(" </Key>\n");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@
import org.keycloak.protocol.ClientInstallationProvider;
import org.keycloak.protocol.saml.SamlClient;
import org.keycloak.protocol.saml.SamlProtocol;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.services.resources.RealmsResource;

import static org.keycloak.protocol.util.ClientCliInstallationUtil.quote;

public class KeycloakSamlSubsystemCliInstallation implements ClientInstallationProvider {

@Override
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, boolean includeSecrets) {
SamlClient samlClient = new SamlClient(client);
StringBuilder builder = new StringBuilder();
String entityId = client.getBaseUrl() == null ? "SPECIFY YOUR entityID!" : client.getBaseUrl();
Expand All @@ -57,7 +58,7 @@ public Response generateInstallation(KeycloakSession session, RealmModel realm,
builder.append("/subsystem=keycloak-saml/secure-deployment=YOUR-WAR.war/SP=")
.append(quote(entityId))
.append("/Key=KEY1:add(signing=true, \\\nPrivateKeyPem=")
.append(quote(samlClient.getClientSigningPrivateKey() == null ? "PRIVATE KEY NOT STORED ON SERVER - REPLACE WITH YOUR OWN PRIVATE KEY" : samlClient.getClientSigningPrivateKey()))
.append(quote(samlClient.getClientSigningPrivateKey() == null ? "PRIVATE KEY NOT STORED ON SERVER - REPLACE WITH YOUR OWN PRIVATE KEY" : includeSecrets ? samlClient.getClientSigningPrivateKey() : ComponentRepresentation.SECRET_VALUE))
.append(", \\\nCertificatePem=")
.append(quote(samlClient.getClientSigningCertificate() == null ? "YOU MUST CONFIGURE YOUR_CLIENT's SIGNING CERTIFICATE" : samlClient.getClientSigningCertificate()))
.append(")\n\n");
Expand All @@ -66,7 +67,7 @@ public Response generateInstallation(KeycloakSession session, RealmModel realm,
builder.append("/subsystem=keycloak-saml/secure-deployment=YOUR-WAR.war/SP=")
.append(quote(entityId))
.append("/Key=KEY2:add(encryption=true,PrivateKeyPem=")
.append(quote(samlClient.getClientEncryptingPrivateKey() == null ? "PRIVATE KEY NOT STORED ON SERVER - REPLACE WITH YOUR OWN PRIVATE KEY" : samlClient.getClientEncryptingPrivateKey()))
.append(quote(samlClient.getClientEncryptingPrivateKey() == null ? "PRIVATE KEY NOT STORED ON SERVER - REPLACE WITH YOUR OWN PRIVATE KEY" : includeSecrets ? samlClient.getClientEncryptingPrivateKey() : ComponentRepresentation.SECRET_VALUE))
.append(")\n\n");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@
public class KeycloakSamlSubsystemInstallation implements ClientInstallationProvider {

@Override
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, boolean includeSecrets) {
SamlClient samlClient = new SamlClient(client);
StringBuilder buffer = new StringBuilder();
buffer.append("<secure-deployment name=\"YOUR-WAR.war\">\n");
KeycloakSamlClientInstallation.baseXml(session, realm, client, baseUri, samlClient, buffer);
KeycloakSamlClientInstallation.baseXml(session, realm, client, baseUri, samlClient, buffer, includeSecrets);
buffer.append("</secure-deployment>\n");
return Response.ok(buffer.toString(), MediaType.TEXT_PLAIN_TYPE).build();
}
Expand Down
Loading
Loading
0