Skip to content

Commit 2351bb3

Browse files
authored
Adding authentication information to access token create APIs (#62490)
* Adding authentication information to access token create APIs Adding authentication object to following APIs: /_security/oauth2/token /_security/delegate_pki /_security/saml/authenticate /_security/oidc/authenticate Resolves: #59685 (cherry picked from commit 51dbd9e) * Addressing PR commends, fixing tests * Returning tokenGroups attribute as SID string instead of byte array (AD metadata) Addressing PR comments * Returning tokenGroups attribute as SID string instead of byte array (AD metadata) Update version check * Returning tokenGroups attribute as SID string instead of byte array (AD metadata) Update version check * Addressing more PR comments * Adding more to integration tests + some small fixes
1 parent 2b4bde4 commit 2351bb3

File tree

28 files changed

+500
-96
lines changed

28 files changed

+500
-96
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java

+24-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import org.elasticsearch.client.security.user.User;
2323
import org.elasticsearch.common.ParseField;
2424
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
25+
import org.elasticsearch.common.xcontent.ToXContentObject;
26+
import org.elasticsearch.common.xcontent.XContentBuilder;
2527
import org.elasticsearch.common.xcontent.XContentParser;
2628

2729
import java.io.IOException;
@@ -38,7 +40,7 @@
3840
* user object contains all user metadata which Elasticsearch uses to map roles,
3941
* etc.
4042
*/
41-
public final class AuthenticateResponse {
43+
public final class AuthenticateResponse implements ToXContentObject {
4244

4345
static final ParseField USERNAME = new ParseField("username");
4446
static final ParseField ROLES = new ParseField("roles");
@@ -123,6 +125,27 @@ public String getAuthenticationType() {
123125
return authenticationType;
124126
}
125127

128+
@Override
129+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
130+
builder.startObject()
131+
.field("username", user.getUsername())
132+
.field("roles", user.getRoles())
133+
.field("metadata", user.getMetadata())
134+
.field("full_name", user.getFullName())
135+
.field("email", user.getEmail())
136+
.field("enabled", enabled);
137+
builder.startObject("authentication_realm")
138+
.field("name", authenticationRealm.name)
139+
.field("type", authenticationRealm.type);
140+
builder.endObject();
141+
builder.startObject("lookup_realm")
142+
.field("name", lookupRealm == null? authenticationRealm.name: lookupRealm.name)
143+
.field("type", lookupRealm == null? authenticationRealm.type: lookupRealm.type);
144+
builder.endObject();
145+
builder.field("authentication_type", authenticationType);
146+
return builder.endObject();
147+
}
148+
126149
@Override
127150
public boolean equals(Object o) {
128151
if (this == o) return true;

client/rest-high-level/src/main/java/org/elasticsearch/client/security/CreateTokenResponse.java

+11-4
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,17 @@ public final class CreateTokenResponse {
4242
private final String scope;
4343
private final String refreshToken;
4444
private final String kerberosAuthenticationResponseToken;
45+
private final AuthenticateResponse authenticationResponse;
4546

4647
public CreateTokenResponse(String accessToken, String type, TimeValue expiresIn, String scope, String refreshToken,
47-
String kerberosAuthenticationResponseToken) {
48+
String kerberosAuthenticationResponseToken, AuthenticateResponse authenticationResponse) {
4849
this.accessToken = accessToken;
4950
this.type = type;
5051
this.expiresIn = expiresIn;
5152
this.scope = scope;
5253
this.refreshToken = refreshToken;
5354
this.kerberosAuthenticationResponseToken = kerberosAuthenticationResponseToken;
55+
this.authenticationResponse = authenticationResponse;
5456
}
5557

5658
public String getAccessToken() {
@@ -77,6 +79,8 @@ public String getKerberosAuthenticationResponseToken() {
7779
return kerberosAuthenticationResponseToken;
7880
}
7981

82+
public AuthenticateResponse getAuthenticationResponse() { return authenticationResponse; }
83+
8084
@Override
8185
public boolean equals(Object o) {
8286
if (this == o) {
@@ -91,17 +95,19 @@ public boolean equals(Object o) {
9195
Objects.equals(expiresIn, that.expiresIn) &&
9296
Objects.equals(scope, that.scope) &&
9397
Objects.equals(refreshToken, that.refreshToken) &&
94-
Objects.equals(kerberosAuthenticationResponseToken, that.kerberosAuthenticationResponseToken);
98+
Objects.equals(kerberosAuthenticationResponseToken, that.kerberosAuthenticationResponseToken)&&
99+
Objects.equals(authenticationResponse, that.authenticationResponse);
95100
}
96101

97102
@Override
98103
public int hashCode() {
99-
return Objects.hash(accessToken, type, expiresIn, scope, refreshToken, kerberosAuthenticationResponseToken);
104+
return Objects.hash(accessToken, type, expiresIn, scope, refreshToken, kerberosAuthenticationResponseToken, authenticationResponse);
100105
}
101106

102107
private static final ConstructingObjectParser<CreateTokenResponse, Void> PARSER = new ConstructingObjectParser<>(
103108
"create_token_response", true, args -> new CreateTokenResponse((String) args[0], (String) args[1],
104-
TimeValue.timeValueSeconds((Long) args[2]), (String) args[3], (String) args[4], (String) args[5]));
109+
TimeValue.timeValueSeconds((Long) args[2]), (String) args[3], (String) args[4], (String) args[5],
110+
(AuthenticateResponse) args[6]));
105111

106112
static {
107113
PARSER.declareString(constructorArg(), new ParseField("access_token"));
@@ -110,6 +116,7 @@ public int hashCode() {
110116
PARSER.declareStringOrNull(optionalConstructorArg(), new ParseField("scope"));
111117
PARSER.declareStringOrNull(optionalConstructorArg(), new ParseField("refresh_token"));
112118
PARSER.declareStringOrNull(optionalConstructorArg(), new ParseField("kerberos_authentication_response_token"));
119+
PARSER.declareObject(constructorArg(), (p, c) -> AuthenticateResponse.fromXContent(p), new ParseField("authentication"));
113120
}
114121

115122
public static CreateTokenResponse fromXContent(XContentParser parser) throws IOException {

client/rest-high-level/src/main/java/org/elasticsearch/client/security/DelegatePkiAuthenticationResponse.java

+13-4
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,14 @@ public final class DelegatePkiAuthenticationResponse {
3434
private final String accessToken;
3535
private final String type;
3636
private final TimeValue expiresIn;
37+
private final AuthenticateResponse authenticationResponse;
3738

38-
public DelegatePkiAuthenticationResponse(String accessToken, String type, TimeValue expiresIn) {
39+
public DelegatePkiAuthenticationResponse(String accessToken, String type, TimeValue expiresIn,
40+
AuthenticateResponse authenticationResponse) {
3941
this.accessToken = accessToken;
4042
this.type = type;
4143
this.expiresIn = expiresIn;
44+
this.authenticationResponse = authenticationResponse;
4245
}
4346

4447
public String getAccessToken() {
@@ -53,6 +56,8 @@ public TimeValue getExpiresIn() {
5356
return expiresIn;
5457
}
5558

59+
public AuthenticateResponse getAuthenticationResponse() { return authenticationResponse; }
60+
5661
@Override
5762
public boolean equals(Object o) {
5863
if (this == o) {
@@ -64,22 +69,26 @@ public boolean equals(Object o) {
6469
final DelegatePkiAuthenticationResponse that = (DelegatePkiAuthenticationResponse) o;
6570
return Objects.equals(accessToken, that.accessToken) &&
6671
Objects.equals(type, that.type) &&
67-
Objects.equals(expiresIn, that.expiresIn);
72+
Objects.equals(expiresIn, that.expiresIn) &&
73+
Objects.equals(authenticationResponse, that.authenticationResponse);
6874
}
6975

7076
@Override
7177
public int hashCode() {
72-
return Objects.hash(accessToken, type, expiresIn);
78+
return Objects.hash(accessToken, type, expiresIn, authenticationResponse);
7379
}
7480

81+
@SuppressWarnings("unchecked")
7582
private static final ConstructingObjectParser<DelegatePkiAuthenticationResponse, Void> PARSER = new ConstructingObjectParser<>(
7683
"delegate_pki_response", true,
77-
args -> new DelegatePkiAuthenticationResponse((String) args[0], (String) args[1], TimeValue.timeValueSeconds((Long) args[2])));
84+
args -> new DelegatePkiAuthenticationResponse((String) args[0], (String) args[1], TimeValue.timeValueSeconds((Long) args[2]),
85+
(AuthenticateResponse) args[3]));
7886

7987
static {
8088
PARSER.declareString(constructorArg(), new ParseField("access_token"));
8189
PARSER.declareString(constructorArg(), new ParseField("type"));
8290
PARSER.declareLong(constructorArg(), new ParseField("expires_in"));
91+
PARSER.declareObject(constructorArg(), (p, c) -> AuthenticateResponse.fromXContent(p), new ParseField("authentication"));
8392
}
8493

8594
public static DelegatePkiAuthenticationResponse fromXContent(XContentParser parser) throws IOException {

client/rest-high-level/src/test/java/org/elasticsearch/client/security/CreateTokenResponseTests.java

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package org.elasticsearch.client.security;
2020

21+
import org.elasticsearch.client.security.user.User;
2122
import org.elasticsearch.common.bytes.BytesReference;
2223
import org.elasticsearch.common.unit.TimeValue;
2324
import org.elasticsearch.common.xcontent.XContentBuilder;
@@ -26,6 +27,7 @@
2627
import org.elasticsearch.test.ESTestCase;
2728

2829
import java.io.IOException;
30+
import java.util.Arrays;
2931

3032
import static org.hamcrest.Matchers.equalTo;
3133

@@ -38,6 +40,10 @@ public void testFromXContent() throws IOException {
3840
final String scope = randomBoolean() ? null : randomAlphaOfLength(4);
3941
final String type = randomAlphaOfLength(6);
4042
final String kerberosAuthenticationResponseToken = randomBoolean() ? null : randomAlphaOfLength(7);
43+
final AuthenticateResponse authenticateResponse = new AuthenticateResponse(new User(randomAlphaOfLength(7),
44+
Arrays.asList( randomAlphaOfLength(9) )),
45+
true, new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(7) ),
46+
new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5) ), "realm");
4147

4248
final XContentType xContentType = randomFrom(XContentType.values());
4349
final XContentBuilder builder = XContentFactory.contentBuilder(xContentType);
@@ -54,6 +60,7 @@ public void testFromXContent() throws IOException {
5460
if (kerberosAuthenticationResponseToken != null) {
5561
builder.field("kerberos_authentication_response_token", kerberosAuthenticationResponseToken);
5662
}
63+
builder.field("authentication", authenticateResponse);
5764
builder.endObject();
5865
BytesReference xContent = BytesReference.bytes(builder);
5966

@@ -64,5 +71,6 @@ public void testFromXContent() throws IOException {
6471
assertThat(response.getType(), equalTo(type));
6572
assertThat(response.getExpiresIn(), equalTo(expiresIn));
6673
assertThat(response.getKerberosAuthenticationResponseToken(), equalTo(kerberosAuthenticationResponseToken));
74+
assertThat(response.getAuthenticationResponse(), equalTo(authenticateResponse));
6775
}
6876
}

client/rest-high-level/src/test/java/org/elasticsearch/client/security/DelegatePkiAuthenticationResponseTests.java

+57-2
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,21 @@
1919

2020
package org.elasticsearch.client.security;
2121

22+
import org.elasticsearch.Version;
2223
import org.elasticsearch.client.AbstractResponseTestCase;
2324
import org.elasticsearch.common.unit.TimeValue;
2425
import org.elasticsearch.common.xcontent.XContentParser;
25-
import org.elasticsearch.client.security.DelegatePkiAuthenticationResponse;
2626
import org.elasticsearch.common.xcontent.XContentType;
27+
import org.elasticsearch.xpack.core.security.authc.Authentication;
28+
import org.elasticsearch.xpack.core.security.user.User;
2729

2830
import java.io.IOException;
31+
import java.util.Arrays;
32+
import java.util.HashMap;
33+
import java.util.Locale;
34+
import java.util.Map;
2935

36+
import static org.hamcrest.Matchers.equalTo;
3037
import static org.hamcrest.Matchers.is;
3138

3239
public class DelegatePkiAuthenticationResponseTests extends
@@ -37,7 +44,8 @@ public class DelegatePkiAuthenticationResponseTests extends
3744
protected org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationResponse createServerTestInstance(
3845
XContentType xContentType) {
3946
return new org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationResponse(randomAlphaOfLength(6),
40-
TimeValue.parseTimeValue(randomTimeValue(), getClass().getSimpleName() + ".expiresIn"));
47+
TimeValue.parseTimeValue(randomTimeValue(), getClass().getSimpleName() + ".expiresIn"),
48+
createAuthentication());
4149
}
4250

4351
@Override
@@ -51,5 +59,52 @@ protected void assertInstances(org.elasticsearch.xpack.core.security.action.Dele
5159
assertThat(serverTestInstance.getAccessToken(), is(clientInstance.getAccessToken()));
5260
assertThat(serverTestInstance.getExpiresIn(), is(clientInstance.getExpiresIn()));
5361
assertThat(clientInstance.getType(), is("Bearer"));
62+
AuthenticateResponse serverAuthenticationResponse = createServerAuthenticationResponse(serverTestInstance.getAuthentication());
63+
User user = serverTestInstance.getAuthentication().getUser();
64+
assertThat(serverAuthenticationResponse, equalTo(clientInstance.getAuthenticationResponse()));
65+
}
66+
67+
protected Authentication createAuthentication() {
68+
final String username = randomAlphaOfLengthBetween(1, 4);
69+
final String[] roles = generateRandomStringArray(4, 4, false, true);
70+
final Map<String, Object> metadata;
71+
metadata = new HashMap<>();
72+
if (randomBoolean()) {
73+
metadata.put("string", null);
74+
} else {
75+
metadata.put("string", randomAlphaOfLengthBetween(0, 4));
76+
}
77+
if (randomBoolean()) {
78+
metadata.put("string_list", null);
79+
} else {
80+
metadata.put("string_list", Arrays.asList(generateRandomStringArray(4, 4, false, true)));
81+
}
82+
final String fullName = randomFrom(random(), null, randomAlphaOfLengthBetween(0, 4));
83+
final String email = randomFrom(random(), null, randomAlphaOfLengthBetween(0, 4));
84+
final boolean enabled = randomBoolean();
85+
final String authenticationRealmName = randomAlphaOfLength(5);
86+
final String authenticationRealmType = randomFrom("file", "native", "ldap", "active_directory", "saml", "kerberos");
87+
final String lookupRealmName = randomAlphaOfLength(5);
88+
final String lookupRealmType = randomFrom("file", "native", "ldap", "active_directory", "saml", "kerberos");
89+
final String nodeName = randomAlphaOfLengthBetween(1, 10);
90+
final Authentication.AuthenticationType authenticationType = randomFrom(Authentication.AuthenticationType.values());
91+
return new Authentication(
92+
new User(username, roles, fullName, email, metadata, true),
93+
new Authentication.RealmRef(authenticationRealmName, authenticationRealmType, nodeName),
94+
new Authentication.RealmRef(lookupRealmName, lookupRealmType, nodeName), Version.CURRENT, authenticationType, metadata);
95+
}
96+
97+
AuthenticateResponse createServerAuthenticationResponse(Authentication authentication){
98+
User user = authentication.getUser();
99+
org.elasticsearch.client.security.user.User cUser = new org.elasticsearch.client.security.user.User(user.principal(),
100+
Arrays.asList(user.roles()), user.metadata(), user.fullName(), user.email());
101+
AuthenticateResponse.RealmInfo authenticatedBy = new AuthenticateResponse.RealmInfo(authentication.getAuthenticatedBy().getName(),
102+
authentication.getAuthenticatedBy().getType());
103+
AuthenticateResponse.RealmInfo lookedUpBy = new AuthenticateResponse.RealmInfo(authentication.getLookedUpBy() == null?
104+
authentication.getAuthenticatedBy().getName(): authentication.getLookedUpBy().getName(),
105+
authentication.getLookedUpBy() == null?
106+
authentication.getAuthenticatedBy().getType(): authentication.getLookedUpBy().getType());
107+
return new AuthenticateResponse(cUser, user.enabled(), authenticatedBy, lookedUpBy,
108+
authentication.getAuthenticationType().toString().toLowerCase(Locale.ROOT));
54109
}
55110
}

docs/java-rest/high-level/security/create-token.asciidoc

+3-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ The returned `CreateTokenResponse` contains the following properties:
4949
`scope`:: The scope of the token. May be `null`.
5050
`refreshToken`:: A secondary "refresh" token that may be used to extend
5151
the life of an access token. May be `null`.
52+
`authentication`:: This is the authentication object for the newly created token. See also
53+
<<{upid}-authenticate-response, authenticate response>> for details.
5254

5355
["source","java",subs="attributes,callouts,macros"]
5456
--------------------------------------------------
@@ -83,4 +85,4 @@ include-tagged::{doc-tests}/SecurityDocumentationIT.java[create-token-execute-li
8385
--------------------------------------------------
8486
<1> Called when the execution is successfully completed. The response is
8587
provided as an argument
86-
<2> Called in case of failure. The raised exception is provided as an argument
88+
<2> Called in case of failure. The raised exception is provided as an argument

docs/java-rest/high-level/security/delegate-pki-authentication.asciidoc

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ The returned +{response}+ contains the following properties:
5252
`type`:: The type of the token, this is always `"Bearer"`.
5353
`expiresIn`:: The length of time (in seconds) until the token will expire.
5454
The token will be considered invalid after that time.
55+
`authentication`:: This is the authentication object for the newly created token. See also
56+
<<{upid}-authenticate-response, authenticate response>> for details.
5557

5658
["source","java",subs="attributes,callouts,macros"]
5759
--------------------------------------------------

x-pack/docs/en/rest-api/security/delegate-pki-authentication.asciidoc

+22-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,28 @@ Which returns the following response:
8989
{
9090
"access_token" : "dGhpcyBpcyBub3QgYSByZWFsIHRva2VuIGJ1dCBpdCBpcyBvbmx5IHRlc3QgZGF0YS4gZG8gbm90IHRyeSB0byByZWFkIHRva2VuIQ==",
9191
"type" : "Bearer",
92-
"expires_in" : 1200
92+
"expires_in" : 1200,
93+
"authentication" : {
94+
"username" : "Elasticsearch Test Client",
95+
"roles" : [ ],
96+
"full_name" : null,
97+
"email" : null,
98+
"metadata" : {
99+
"pki_dn" : "O=org, OU=Elasticsearch, CN=Elasticsearch Test Client",
100+
"pki_delegated_by_user" : "test_admin",
101+
"pki_delegated_by_realm" : "file"
102+
},
103+
"enabled" : true,
104+
"authentication_realm" : {
105+
"name" : "pki1",
106+
"type" : "pki"
107+
},
108+
"lookup_realm" : {
109+
"name" : "pki1",
110+
"type" : "pki"
111+
},
112+
"authentication_type" : "realm"
113+
}
93114
}
94115
--------------------------------------------------
95116
// TESTRESPONSE[s/dGhpcyBpcyBub3QgYSByZWFsIHRva2VuIGJ1dCBpdCBpcyBvbmx5IHRlc3QgZGF0YS4gZG8gbm90IHRyeSB0byByZWFkIHRva2VuIQ==/$body.access_token/]

0 commit comments

Comments
 (0)