-
Notifications
You must be signed in to change notification settings - Fork 24.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add IDP Configuration settings and related tests #51682
Changes from 5 commits
57d2d76
fcb2e8c
db7cb67
8ed79cb
a4136c7
5252c8b
00caa85
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.idp.saml.idp; | ||
|
||
import org.elasticsearch.common.Strings; | ||
import org.elasticsearch.common.settings.Setting; | ||
import org.elasticsearch.common.settings.Settings; | ||
import org.elasticsearch.common.settings.SettingsException; | ||
import org.elasticsearch.common.util.iterable.Iterables; | ||
import org.elasticsearch.env.Environment; | ||
import org.elasticsearch.xpack.core.ssl.CertParsingUtils; | ||
import org.elasticsearch.xpack.core.ssl.X509KeyPairSettings; | ||
import org.opensaml.security.x509.X509Credential; | ||
import org.opensaml.security.x509.impl.X509KeyManagerX509CredentialAdapter; | ||
|
||
import javax.net.ssl.X509KeyManager; | ||
import java.security.PrivateKey; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
import static org.elasticsearch.xpack.idp.IdentityProviderPlugin.IDP_ENTITY_ID; | ||
import static org.elasticsearch.xpack.idp.IdentityProviderPlugin.IDP_SIGNING_KEY_ALIAS; | ||
import static org.elasticsearch.xpack.idp.IdentityProviderPlugin.IDP_SLO_POST_ENDPOINT; | ||
import static org.elasticsearch.xpack.idp.IdentityProviderPlugin.IDP_SLO_REDIRECT_ENDPOINT; | ||
import static org.elasticsearch.xpack.idp.IdentityProviderPlugin.IDP_SSO_POST_ENDPOINT; | ||
import static org.elasticsearch.xpack.idp.IdentityProviderPlugin.IDP_SSO_REDIRECT_ENDPOINT; | ||
|
||
public class CloudIdp implements SamlIdentityProvider { | ||
|
||
private final String entityId; | ||
private final HashMap<String, String> ssoEndpoints = new HashMap<>(); | ||
private final HashMap<String, String> sloEndpoints = new HashMap<>(); | ||
private final X509Credential signingCredential; | ||
|
||
public CloudIdp(Environment env, Settings settings) { | ||
this.entityId = require(settings, IDP_ENTITY_ID); | ||
this.ssoEndpoints.put("redirect", require(settings, IDP_SSO_REDIRECT_ENDPOINT)); | ||
this.ssoEndpoints.computeIfAbsent("post", v -> settings.get(IDP_SSO_POST_ENDPOINT.getKey())); | ||
this.sloEndpoints.computeIfAbsent("post", v -> settings.get(IDP_SLO_POST_ENDPOINT.getKey())); | ||
this.sloEndpoints.computeIfAbsent("redirect", v -> settings.get(IDP_SLO_REDIRECT_ENDPOINT.getKey())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand why we have 1 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The "added benefit" of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like it's depending on a side effect (a documented one, but still a side effect) and makes the code less clear because the reader is left trying to understand why we're using different methods. |
||
this.signingCredential = buildSigningCredential(env, settings); | ||
} | ||
|
||
@Override | ||
public String getEntityId() { | ||
return entityId; | ||
} | ||
|
||
@Override | ||
public String getSingleSignOnEndpoint(String binding) { | ||
return ssoEndpoints.get(binding); | ||
} | ||
|
||
@Override | ||
public String getSingleLogoutEndpoint(String binding) { | ||
return sloEndpoints.get(binding); | ||
} | ||
|
||
@Override | ||
public X509Credential getSigningCredential() { | ||
return signingCredential; | ||
} | ||
|
||
private static String require(Settings settings, Setting<String> setting) { | ||
if (settings.hasValue(setting.getKey())) { | ||
return setting.get(settings); | ||
} else { | ||
throw new SettingsException("The configuration setting [" + setting.getKey() + "] is required"); | ||
} | ||
} | ||
|
||
static X509Credential buildSigningCredential(Environment env, Settings settings) { | ||
final X509KeyPairSettings keyPairSettings = X509KeyPairSettings.withPrefix("xpack.idp.signing.", false); | ||
final X509KeyManager keyManager = CertParsingUtils.getKeyManager(keyPairSettings, settings, null, env); | ||
if (keyManager == null) { | ||
return null; | ||
} | ||
|
||
final String selectedAlias; | ||
final String configAlias = IDP_SIGNING_KEY_ALIAS.get(settings); | ||
if (Strings.isNullOrEmpty(configAlias)) { | ||
final Set<String> aliases = new HashSet<>(); | ||
final String[] rsaAliases = keyManager.getServerAliases("RSA", null); | ||
if (null != rsaAliases) { | ||
aliases.addAll(Arrays.asList(rsaAliases)); | ||
} | ||
final String[] ecAliases = keyManager.getServerAliases("EC", null); | ||
if (null != ecAliases) { | ||
aliases.addAll(Arrays.asList(ecAliases)); | ||
} | ||
if (aliases.isEmpty()) { | ||
throw new IllegalArgumentException( | ||
"The configured keystore for xpack.idp.signing.keystore does not contain any RSA or EC key pairs"); | ||
} | ||
if (aliases.size() > 1) { | ||
throw new IllegalArgumentException("The configured keystore for xpack.idp.signing.keystore contains multiple key pairs" + | ||
" but no alias has been configured with [" + IDP_SIGNING_KEY_ALIAS.getKey() + "]"); | ||
jkakavas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
selectedAlias = Iterables.get(aliases, 0); | ||
} else { | ||
selectedAlias = configAlias; | ||
} | ||
final PrivateKey signingKey = keyManager.getPrivateKey(selectedAlias); | ||
if (signingKey == null) { | ||
throw new IllegalArgumentException("The configured keystore for xpack.idp.signing.keystore does not have a private key" + | ||
" associated with alias [" + selectedAlias + "]"); | ||
} | ||
|
||
final String keyType = signingKey.getAlgorithm(); | ||
if (keyType.equals("RSA") == false && keyType.equals("EC") == false) { | ||
throw new IllegalArgumentException("The key associated with alias [" + selectedAlias + "] " + "that has been configured with [" | ||
+ IDP_SIGNING_KEY_ALIAS.getKey() + "] uses unsupported key algorithm type [" + keyType | ||
+ "], only RSA and EC are supported"); | ||
} | ||
return new X509KeyManagerX509CredentialAdapter(keyManager, selectedAlias); | ||
} | ||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,10 +7,18 @@ | |
package org.elasticsearch.xpack.idp.saml.idp; | ||
|
||
|
||
import org.opensaml.security.x509.X509Credential; | ||
|
||
/** | ||
* SAML 2.0 configuration information about this IdP | ||
*/ | ||
public interface SamlIdentityProvider { | ||
|
||
String getEntityId(); | ||
|
||
String getSingleSignOnEndpoint(String binding); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could these return Also where do we expect to need these? Is it just for metadata? (which is a real need - I just want to check how we expect to consume them). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see #51682 (comment)
Metadata creation and incoming SAML message validation |
||
|
||
String getSingleLogoutEndpoint(String binding); | ||
|
||
X509Credential getSigningCredential(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we make these
Setting<URI>
instead?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went back and forth. I settled on that we care about them being URIs mainly when we validate them while reading the setting and from there on we can treat them as Strings. Since I don't see any other use for these other than:
it feels that future implementers or anyone reading the code doesn't need to care about the fact that these are URIs so I opted for the "simpler" handling.