Skip to content

Commit 300f523

Browse files
committed
Merge branch 'oidc-realm-authentication-flows' into oidc-realm-logout
2 parents 56b86eb + 09413b5 commit 300f523

35 files changed

+4615
-206
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/oidc/OpenIdConnectRealmSettings.java

Lines changed: 144 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@
77

88
import org.elasticsearch.common.settings.SecureString;
99
import org.elasticsearch.common.settings.Setting;
10+
import org.elasticsearch.common.unit.TimeValue;
1011
import org.elasticsearch.common.util.set.Sets;
12+
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
1113
import org.elasticsearch.xpack.core.security.authc.RealmSettings;
1214
import org.elasticsearch.xpack.core.security.authc.support.DelegatedAuthorizationSettings;
15+
import org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings;
1316

1417
import java.net.URI;
1518
import java.net.URISyntaxException;
19+
import java.util.Arrays;
20+
import java.util.Collection;
1621
import java.util.Collections;
1722
import java.util.List;
1823
import java.util.Set;
@@ -24,25 +29,64 @@ public class OpenIdConnectRealmSettings {
2429
private OpenIdConnectRealmSettings() {
2530
}
2631

32+
private static final List<String> signingAlgorithms = Collections.unmodifiableList(
33+
Arrays.asList("HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "PS256", "PS384", "PS512"));
34+
private static final List<String> responseTypes = Arrays.asList("code", "id_token", "id_token token");
2735
public static final String TYPE = "oidc";
2836

29-
public static final Setting.AffixSetting<String> OP_NAME
30-
= RealmSettings.simpleString(TYPE, "op.name", Setting.Property.NodeScope);
3137
public static final Setting.AffixSetting<String> RP_CLIENT_ID
3238
= RealmSettings.simpleString(TYPE, "rp.client_id", Setting.Property.NodeScope);
3339
public static final Setting.AffixSetting<SecureString> RP_CLIENT_SECRET
3440
= RealmSettings.secureString(TYPE, "rp.client_secret");
3541
public static final Setting.AffixSetting<String> RP_REDIRECT_URI
36-
= RealmSettings.simpleString(TYPE, "rp.redirect_uri", Setting.Property.NodeScope);
42+
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "rp.redirect_uri",
43+
key -> Setting.simpleString(key, v -> {
44+
try {
45+
new URI(v);
46+
} catch (URISyntaxException e) {
47+
throw new IllegalArgumentException("Invalid value [" + v + "] for [" + key + "]. Not a valid URI.", e);
48+
}
49+
}, Setting.Property.NodeScope));
3750
public static final Setting.AffixSetting<String> RP_RESPONSE_TYPE
38-
= RealmSettings.simpleString(TYPE, "rp.response_type", Setting.Property.NodeScope);
51+
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "rp.response_type",
52+
key -> Setting.simpleString(key, v -> {
53+
if (responseTypes.contains(v) == false) {
54+
throw new IllegalArgumentException("Invalid value [" + v + "] for [" + key + "]. Allowed values are " + responseTypes + "");
55+
}
56+
}, Setting.Property.NodeScope));
57+
public static final Setting.AffixSetting<String> RP_SIGNATURE_VERIFICATION_ALGORITHM
58+
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "rp.signature_verification_algorithm",
59+
key -> new Setting<>(key, "RS256", Function.identity(), v -> {
60+
if (signingAlgorithms.contains(v) == false) {
61+
throw new IllegalArgumentException(
62+
"Invalid value [" + v + "] for [" + key + "]. Allowed values are " + signingAlgorithms + "}]");
63+
}
64+
}, Setting.Property.NodeScope));
65+
public static final Setting.AffixSetting<List<String>> RP_REQUESTED_SCOPES = Setting.affixKeySetting(
66+
RealmSettings.realmSettingPrefix(TYPE), "rp.requested_scopes",
67+
key -> Setting.listSetting(key, Collections.singletonList("openid"), Function.identity(), Setting.Property.NodeScope));
68+
69+
public static final Setting.AffixSetting<String> OP_NAME
70+
= RealmSettings.simpleString(TYPE, "op.name", Setting.Property.NodeScope);
3971
public static final Setting.AffixSetting<String> OP_AUTHORIZATION_ENDPOINT
40-
= RealmSettings.simpleString(TYPE, "op.authorization_endpoint", Setting.Property.NodeScope);
72+
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "op.authorization_endpoint",
73+
key -> Setting.simpleString(key, v -> {
74+
try {
75+
new URI(v);
76+
} catch (URISyntaxException e) {
77+
throw new IllegalArgumentException("Invalid value [" + v + "] for [" + key + "]. Not a valid URI.", e);
78+
}
79+
}, Setting.Property.NodeScope));
4180
public static final Setting.AffixSetting<String> OP_TOKEN_ENDPOINT
42-
= RealmSettings.simpleString(TYPE, "op.token_endpoint", Setting.Property.NodeScope);
81+
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "op.token_endpoint",
82+
key -> Setting.simpleString(key, v -> {
83+
try {
84+
new URI(v);
85+
} catch (URISyntaxException e) {
86+
throw new IllegalArgumentException("Invalid value [" + v + "] for [" + key + "]. Not a valid URI.", e);
87+
}
88+
}, Setting.Property.NodeScope));
4389
public static final Setting.AffixSetting<String> OP_USERINFO_ENDPOINT
44-
= RealmSettings.simpleString(TYPE, "op.userinfo_endpoint", Setting.Property.NodeScope);
45-
public static final Setting.AffixSetting<String> OP_ENDSESSION_ENDPOINT
4690
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "op.userinfo_endpoint",
4791
key -> Setting.simpleString(key, v -> {
4892
try {
@@ -51,18 +95,105 @@ private OpenIdConnectRealmSettings() {
5195
throw new IllegalArgumentException("Invalid value [" + v + "] for [" + key + "]. Not a valid URI.", e);
5296
}
5397
}, Setting.Property.NodeScope));
98+
public static final Setting.AffixSetting<String> OP_ENDSESSION_ENDPOINT
99+
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "op.endsession_endpoint",
100+
key -> Setting.simpleString(key, v -> {
101+
try {
102+
new URI(v);
103+
} catch (URISyntaxException e) {
104+
throw new IllegalArgumentException("Invalid value [" + v + "] for [" + key + "]. Not a valid URI.", e);
105+
}
106+
}, Setting.Property.NodeScope));
54107
public static final Setting.AffixSetting<String> OP_ISSUER
55108
= RealmSettings.simpleString(TYPE, "op.issuer", Setting.Property.NodeScope);
56-
public static final Setting.AffixSetting<List<String>> RP_REQUESTED_SCOPES = Setting.affixKeySetting(
57-
RealmSettings.realmSettingPrefix(TYPE), "rp.requested_scopes",
58-
key -> Setting.listSetting(key, Collections.singletonList("openid"), Function.identity(), Setting.Property.NodeScope));
109+
public static final Setting.AffixSetting<String> OP_JWKSET_PATH
110+
= RealmSettings.simpleString(TYPE, "op.jwkset_path", Setting.Property.NodeScope);
111+
112+
public static final Setting.AffixSetting<TimeValue> ALLOWED_CLOCK_SKEW
113+
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "allowed_clock_skew",
114+
key -> Setting.timeSetting(key, TimeValue.timeValueSeconds(60), Setting.Property.NodeScope));
115+
public static final Setting.AffixSetting<Boolean> POPULATE_USER_METADATA = Setting.affixKeySetting(
116+
RealmSettings.realmSettingPrefix(TYPE), "populate_user_metadata",
117+
key -> Setting.boolSetting(key, true, Setting.Property.NodeScope));
118+
private static final TimeValue DEFAULT_TIMEOUT = TimeValue.timeValueSeconds(5);
119+
public static final Setting.AffixSetting<TimeValue> HTTP_CONNECT_TIMEOUT
120+
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "http.connect_timeout",
121+
key -> Setting.timeSetting(key, DEFAULT_TIMEOUT, Setting.Property.NodeScope));
122+
public static final Setting.AffixSetting<TimeValue> HTTP_CONNECTION_READ_TIMEOUT
123+
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "http.connection_read_timeout",
124+
key -> Setting.timeSetting(key, DEFAULT_TIMEOUT, Setting.Property.NodeScope));
125+
public static final Setting.AffixSetting<TimeValue> HTTP_SOCKET_TIMEOUT
126+
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "http.socket_timeout",
127+
key -> Setting.timeSetting(key, DEFAULT_TIMEOUT, Setting.Property.NodeScope));
128+
public static final Setting.AffixSetting<Integer> HTTP_MAX_CONNECTIONS
129+
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "http.max_connections",
130+
key -> Setting.intSetting(key, 200, Setting.Property.NodeScope));
131+
public static final Setting.AffixSetting<Integer> HTTP_MAX_ENDPOINT_CONNECTIONS
132+
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "http.max_endpoint_connections",
133+
key -> Setting.intSetting(key, 200, Setting.Property.NodeScope));
134+
135+
public static final ClaimSetting PRINCIPAL_CLAIM = new ClaimSetting("principal");
136+
public static final ClaimSetting GROUPS_CLAIM = new ClaimSetting("groups");
137+
public static final ClaimSetting NAME_CLAIM = new ClaimSetting("name");
138+
public static final ClaimSetting DN_CLAIM = new ClaimSetting("dn");
139+
public static final ClaimSetting MAIL_CLAIM = new ClaimSetting("mail");
59140

60141
public static Set<Setting.AffixSetting<?>> getSettings() {
61142
final Set<Setting.AffixSetting<?>> set = Sets.newHashSet(
62-
OP_NAME, RP_CLIENT_ID, RP_REDIRECT_URI, RP_RESPONSE_TYPE, RP_REQUESTED_SCOPES, RP_CLIENT_SECRET,
63-
OP_AUTHORIZATION_ENDPOINT, OP_TOKEN_ENDPOINT, OP_USERINFO_ENDPOINT, OP_ISSUER);
143+
RP_CLIENT_ID, RP_REDIRECT_URI, RP_RESPONSE_TYPE, RP_REQUESTED_SCOPES, RP_CLIENT_SECRET, RP_SIGNATURE_VERIFICATION_ALGORITHM,
144+
OP_NAME, OP_AUTHORIZATION_ENDPOINT, OP_TOKEN_ENDPOINT, OP_USERINFO_ENDPOINT, OP_ENDSESSION_ENDPOINT, OP_ISSUER,
145+
OP_JWKSET_PATH, HTTP_CONNECT_TIMEOUT, HTTP_CONNECTION_READ_TIMEOUT, HTTP_SOCKET_TIMEOUT, HTTP_MAX_CONNECTIONS,
146+
HTTP_MAX_ENDPOINT_CONNECTIONS, ALLOWED_CLOCK_SKEW);
64147
set.addAll(DelegatedAuthorizationSettings.getSettings(TYPE));
65148
set.addAll(RealmSettings.getStandardSettings(TYPE));
149+
set.addAll(SSLConfigurationSettings.getRealmSettings(TYPE));
150+
set.addAll(PRINCIPAL_CLAIM.settings());
151+
set.addAll(GROUPS_CLAIM.settings());
152+
set.addAll(DN_CLAIM.settings());
153+
set.addAll(NAME_CLAIM.settings());
154+
set.addAll(MAIL_CLAIM.settings());
66155
return set;
67156
}
157+
158+
159+
/**
160+
* The OIDC realm offers a number of settings that rely on claim values that are populated by the OP in the ID Token or the User Info
161+
* response.
162+
* Each claim has 2 settings:
163+
* <ul>
164+
* <li>The name of the OpenID Connect claim to use</li>
165+
* <li>An optional java pattern (regex) to apply to that claim value in order to extract the substring that should be used.</li>
166+
* </ul>
167+
* For example, the Elasticsearch User Principal could be configured to come from the OpenID Connect standard claim "email",
168+
* and extract only the local-part of the user's email address (i.e. the name before the '@').
169+
* This class encapsulates those 2 settings.
170+
*/
171+
public static final class ClaimSetting {
172+
public static final String CLAIMS_PREFIX = "claims.";
173+
public static final String CLAIM_PATTERNS_PREFIX = "claim_patterns.";
174+
175+
private final Setting.AffixSetting<String> claim;
176+
private final Setting.AffixSetting<String> pattern;
177+
178+
public ClaimSetting(String name) {
179+
claim = RealmSettings.simpleString(TYPE, CLAIMS_PREFIX + name, Setting.Property.NodeScope);
180+
pattern = RealmSettings.simpleString(TYPE, CLAIM_PATTERNS_PREFIX + name, Setting.Property.NodeScope);
181+
}
182+
183+
public Collection<Setting.AffixSetting<?>> settings() {
184+
return Arrays.asList(getClaim(), getPattern());
185+
}
186+
187+
public String name(RealmConfig config) {
188+
return getClaim().getConcreteSettingForNamespace(config.name()).getKey();
189+
}
190+
191+
public Setting.AffixSetting<String> getClaim() {
192+
return claim;
193+
}
194+
195+
public Setting.AffixSetting<String> getPattern() {
196+
return pattern;
197+
}
198+
}
68199
}

x-pack/plugin/security/build.gradle

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ dependencies {
5656
compile "org.apache.httpcomponents:httpclient-cache:${versions.httpclient}"
5757
compile 'com.google.guava:guava:19.0'
5858

59+
// Dependencies for oidc
60+
compile "com.nimbusds:oauth2-oidc-sdk:6.5"
61+
compile "com.nimbusds:nimbus-jose-jwt:4.41.2"
62+
compile "com.nimbusds:lang-tag:1.4.4"
63+
compile "com.sun.mail:javax.mail:1.6.2"
64+
compile "net.jcip:jcip-annotations:1.0"
65+
compile "net.minidev:json-smart:2.3"
66+
compile "net.minidev:accessors-smart:1.2"
67+
68+
5969
testCompile 'org.elasticsearch:securemock:1.2'
6070
testCompile "org.elasticsearch:mocksocket:${versions.mocksocket}"
6171
//testCompile "org.yaml:snakeyaml:${versions.snakeyaml}"
@@ -160,7 +170,7 @@ forbiddenPatterns {
160170
}
161171

162172
forbiddenApisMain {
163-
signaturesFiles += files('forbidden/ldap-signatures.txt', 'forbidden/xml-signatures.txt')
173+
signaturesFiles += files('forbidden/ldap-signatures.txt', 'forbidden/xml-signatures.txt', 'forbidden/oidc-signatures.txt')
164174
}
165175

166176
// classes are missing, e.g. com.ibm.icu.lang.UCharacter
@@ -257,7 +267,13 @@ thirdPartyAudit {
257267
'net.sf.ehcache.Ehcache',
258268
'net.sf.ehcache.Element',
259269
// [missing classes] SLF4j includes an optional class that depends on an extension class (!)
260-
'org.slf4j.ext.EventData'
270+
'org.slf4j.ext.EventData',
271+
'org.cryptomator.siv.SivMode',
272+
'org.objectweb.asm.ClassWriter',
273+
'org.objectweb.asm.Label',
274+
'org.objectweb.asm.MethodVisitor',
275+
'org.objectweb.asm.Type'
276+
261277
)
262278

263279
ignoreViolations (
@@ -278,7 +294,13 @@ if (project.runtimeJavaVersion > JavaVersion.VERSION_1_8) {
278294
'javax.xml.bind.JAXBElement',
279295
'javax.xml.bind.JAXBException',
280296
'javax.xml.bind.Unmarshaller',
281-
'javax.xml.bind.UnmarshallerHandler'
297+
'javax.xml.bind.UnmarshallerHandler',
298+
'javax.activation.ActivationDataFlavor',
299+
'javax.activation.DataContentHandler',
300+
'javax.activation.DataHandler',
301+
'javax.activation.DataSource',
302+
'javax.activation.FileDataSource',
303+
'javax.activation.FileTypeMap'
282304
)
283305
}
284306

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@defaultMessage Blocking methods should not be used for HTTP requests. Use CloseableHttpAsyncClient instead
2+
com.nimbusds.oauth2.sdk.http.HTTPRequest#send(javax.net.ssl.HostnameVerifier, javax.net.ssl.SSLSocketFactory)
3+
com.nimbusds.oauth2.sdk.http.HTTPRequest#send()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
c592b500269bfde36096641b01238a8350f8aa31

0 commit comments

Comments
 (0)