Skip to content
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

Return API key name in _authentication response #78946

Merged
merged 38 commits into from
Oct 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
2442918
Return API KEY name in _authentication response
justincr-elastic Oct 11, 2021
bc1bac4
Return API KEY name in _authentication response
justincr-elastic Oct 11, 2021
037c338
Merge remote-tracking branch 'origin/master' into feature/70306
justincr-elastic Oct 12, 2021
945cb34
Resolve PR feedback.
justincr-elastic Oct 14, 2021
53c428d
Merge remote-tracking branch 'origin/master' into feature/70306
justincr-elastic Oct 14, 2021
6339ad6
Add api_key id+name in Authentication metadata.
justincr-elastic Oct 15, 2021
b81ae17
Merge remote-tracking branch 'origin/master' into feature/70306
justincr-elastic Oct 15, 2021
fa4542c
Fix another test lacking mandatory api_key id.
justincr-elastic Oct 15, 2021
6dfe2a7
Make API KEY info map validation code reusable.
justincr-elastic Oct 15, 2021
940a9cb
Remove unused method.
justincr-elastic Oct 15, 2021
914f6bd
Merge branch 'master' into feature/70306
elasticmachine Oct 15, 2021
70dbd86
Fix client tests.
justincr-elastic Oct 15, 2021
95a7777
Remove whitespace edits.
justincr-elastic Oct 15, 2021
ae6972e
Merge remote-tracking branch 'origin/master' into feature/70306
justincr-elastic Oct 15, 2021
707c774
Remove unused import.
justincr-elastic Oct 15, 2021
162cf7b
Merge branch 'master' into feature/70306
elasticmachine Oct 18, 2021
2c6b5ef
Merge remote-tracking branch 'origin/master' into feature/70306
justincr-elastic Oct 20, 2021
328c544
Move ApiKeyServiceFields to AuthenticationFields
justincr-elastic Oct 20, 2021
82d3f8f
Fix refactoring. It mixed single+wildcard imports.
justincr-elastic Oct 20, 2021
0607b15
Merge branch 'master' into feature/70306
elasticmachine Oct 20, 2021
3f98ccd
Resolve PR comments.
justincr-elastic Oct 20, 2021
2f0bee8
Re-add public constructors.
justincr-elastic Oct 21, 2021
036a753
Guard assertions with authentication type check.
justincr-elastic Oct 21, 2021
2c7d898
Re-add asserts to simplify production null checks.
justincr-elastic Oct 21, 2021
015822e
Fix assertions.
justincr-elastic Oct 21, 2021
a4fd654
API KEY name must be present, null or non-null.
justincr-elastic Oct 21, 2021
0e0f30c
Fix test to include API KEY name, random or null.
justincr-elastic Oct 21, 2021
6cab25f
Merge branch 'master' into feature/70306
elasticmachine Oct 21, 2021
ac36557
Fix tests.
justincr-elastic Oct 22, 2021
15e140a
Fix test.
justincr-elastic Oct 25, 2021
d99eeab
Merge commit '4e09186' into feature/70306
justincr-elastic Oct 28, 2021
7ff316e
Merge commit '12ad399' into feature/70306
justincr-elastic Oct 28, 2021
1ad6e08
Merge branch 'master' into feature/70306
justincr-elastic Oct 28, 2021
96884c1
Fix issues with auto-merge.
justincr-elastic Oct 28, 2021
cf344e8
Apply new whitesource formatting rules.
justincr-elastic Oct 28, 2021
2c15645
Put null instead of remove for API KEY name
justincr-elastic Oct 28, 2021
3e4b468
Deserialize authentication.api_key.name=null
justincr-elastic Oct 28, 2021
582f733
Merge branch 'master' into feature/70306
elasticmachine Oct 29, 2021
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 @@ -44,6 +44,9 @@ public final class AuthenticateResponse implements ToXContentObject {
static final ParseField REALM_TYPE = new ParseField("type");
static final ParseField AUTHENTICATION_TYPE = new ParseField("authentication_type");
static final ParseField TOKEN = new ParseField("token");
static final ParseField API_KEY_INFO = new ParseField("api_key"); // authentication.api_key={"id":"abc123","name":"my-api-key"}
static final ParseField API_KEY_INFO_ID = new ParseField("id");
static final ParseField API_KEY_INFO_NAME = new ParseField("name");

@SuppressWarnings("unchecked")
private static final ConstructingObjectParser<AuthenticateResponse, Void> PARSER = new ConstructingObjectParser<>(
Expand All @@ -55,7 +58,8 @@ public final class AuthenticateResponse implements ToXContentObject {
(RealmInfo) a[6],
(RealmInfo) a[7],
(String) a[8],
(Map<String, Object>) a[9]
(Map<String, Object>) a[9],
(ApiKeyInfo) a[10]
)
);
static {
Expand All @@ -66,6 +70,15 @@ public final class AuthenticateResponse implements ToXContentObject {
);
realmInfoParser.declareString(constructorArg(), REALM_NAME);
realmInfoParser.declareString(constructorArg(), REALM_TYPE);

final ConstructingObjectParser<ApiKeyInfo, Void> apiKeyInfoParser = new ConstructingObjectParser<>(
"api_key",
true,
a -> new ApiKeyInfo((String) a[0], (String) a[1])
);
apiKeyInfoParser.declareString(constructorArg(), API_KEY_INFO_ID);
apiKeyInfoParser.declareString(optionalConstructorArg(), API_KEY_INFO_NAME);

PARSER.declareString(constructorArg(), USERNAME);
PARSER.declareStringArray(constructorArg(), ROLES);
PARSER.<Map<String, Object>>declareObject(constructorArg(), (parser, c) -> parser.map(), METADATA);
Expand All @@ -76,6 +89,7 @@ public final class AuthenticateResponse implements ToXContentObject {
PARSER.declareObject(constructorArg(), realmInfoParser, LOOKUP_REALM);
PARSER.declareString(constructorArg(), AUTHENTICATION_TYPE);
PARSER.declareObjectOrNull(optionalConstructorArg(), (p, c) -> p.map(), null, TOKEN);
PARSER.declareObjectOrNull(optionalConstructorArg(), apiKeyInfoParser, null, API_KEY_INFO);
}

private final User user;
Expand All @@ -85,6 +99,8 @@ public final class AuthenticateResponse implements ToXContentObject {
private final String authenticationType;
@Nullable
private final Map<String, Object> token;
@Nullable
private final ApiKeyInfo apikeyinfo;

public AuthenticateResponse(
User user,
Expand All @@ -103,13 +119,26 @@ public AuthenticateResponse(
RealmInfo lookupRealm,
String authenticationType,
@Nullable Map<String, Object> token
) {
this(user, enabled, authenticationRealm, lookupRealm, authenticationType, null, null);
}

public AuthenticateResponse(
User user,
boolean enabled,
RealmInfo authenticationRealm,
RealmInfo lookupRealm,
String authenticationType,
@Nullable Map<String, Object> token,
@Nullable ApiKeyInfo apikeyinfo
) {
this.user = user;
this.enabled = enabled;
this.authenticationRealm = authenticationRealm;
this.lookupRealm = lookupRealm;
this.authenticationType = authenticationType;
this.token = token;
this.apikeyinfo = apikeyinfo;
}

/**
Expand Down Expand Up @@ -150,6 +179,10 @@ public Map<String, Object> getToken() {
return token;
}

public ApiKeyInfo getApiKeyInfo() {
return apikeyinfo;
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
Expand All @@ -175,6 +208,14 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
if (token != null) {
builder.field(AuthenticateResponse.TOKEN.getPreferredName(), token);
}
if (apikeyinfo != null) {
builder.startObject(AuthenticateResponse.API_KEY_INFO.getPreferredName());
builder.field(AuthenticateResponse.API_KEY_INFO_ID.getPreferredName(), apikeyinfo.getId());
if (apikeyinfo.getName() != null) {
builder.field(AuthenticateResponse.API_KEY_INFO_NAME.getPreferredName(), apikeyinfo.getName());
}
builder.endObject();
}
return builder.endObject();
}

Expand All @@ -188,12 +229,13 @@ public boolean equals(Object o) {
&& Objects.equals(authenticationRealm, that.authenticationRealm)
&& Objects.equals(lookupRealm, that.lookupRealm)
&& Objects.equals(authenticationType, that.authenticationType)
&& Objects.equals(token, that.token);
&& Objects.equals(token, that.token)
&& Objects.equals(apikeyinfo, that.apikeyinfo);
}

@Override
public int hashCode() {
return Objects.hash(user, enabled, authenticationRealm, lookupRealm, authenticationType, token);
return Objects.hash(user, enabled, authenticationRealm, lookupRealm, authenticationType, token, apikeyinfo);
}

public static AuthenticateResponse fromXContent(XContentParser parser) throws IOException {
Expand Down Expand Up @@ -230,4 +272,35 @@ public int hashCode() {
return Objects.hash(name, type);
}
}

public static class ApiKeyInfo { // authentication.api_key={"id":"abc123","name":"my-api-key"}
private String id;
private String name;

ApiKeyInfo(String id, String name) {
this.id = id;
this.name = name;
}

public String getId() {
return id;
}

public String getName() {
return name;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final ApiKeyInfo that = (ApiKeyInfo) o;
return Objects.equals(this.id, that.id) && Objects.equals(this.name, that.name);
}

@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,23 @@ protected AuthenticateResponse createTestInstance() {

final String authenticationType = randomFrom("realm", "api_key", "token", "anonymous", "internal");

final AuthenticateResponse.ApiKeyInfo apiKeyInfo;
if ("api_key".equals(authenticationType)) {
final String apiKeyId = randomAlphaOfLength(16); // mandatory
final String apiKeyName = randomBoolean() ? randomAlphaOfLength(20) : null; // optional
apiKeyInfo = new AuthenticateResponse.ApiKeyInfo(apiKeyId, apiKeyName);
} else {
apiKeyInfo = null;
}

return new AuthenticateResponse(
new User(username, roles, metadata, fullName, email),
enabled,
authenticationRealm,
lookupRealm,
authenticationType,
tokenInfo
tokenInfo,
apiKeyInfo
);
}

Expand All @@ -106,13 +116,15 @@ private AuthenticateResponse copy(AuthenticateResponse response) {
response.getAuthenticationRealm(),
response.getLookupRealm(),
response.getAuthenticationType(),
Map.copyOf(response.getToken())
Map.copyOf(response.getToken()),
response.getApiKeyInfo()
);
}

private AuthenticateResponse mutate(AuthenticateResponse response) {
final User originalUser = response.getUser();
switch (randomIntBetween(1, 10)) {
int randomSwitchCase = randomIntBetween(1, 11); // range is inclusive
switch (randomSwitchCase) {
case 1:
return new AuthenticateResponse(
new User(
Expand All @@ -126,7 +138,8 @@ private AuthenticateResponse mutate(AuthenticateResponse response) {
response.getAuthenticationRealm(),
response.getLookupRealm(),
response.getAuthenticationType(),
response.getToken()
response.getToken(),
response.getApiKeyInfo()
);
case 2:
final List<String> wrongRoles = new ArrayList<>(originalUser.getRoles());
Expand All @@ -143,7 +156,8 @@ private AuthenticateResponse mutate(AuthenticateResponse response) {
response.getAuthenticationRealm(),
response.getLookupRealm(),
response.getAuthenticationType(),
response.getToken()
response.getToken(),
response.getApiKeyInfo()
);
case 3:
final Map<String, Object> wrongMetadata = new HashMap<>(originalUser.getMetadata());
Expand All @@ -160,7 +174,8 @@ private AuthenticateResponse mutate(AuthenticateResponse response) {
response.getAuthenticationRealm(),
response.getLookupRealm(),
response.getAuthenticationType(),
response.getToken()
response.getToken(),
response.getApiKeyInfo()
);
case 4:
return new AuthenticateResponse(
Expand All @@ -174,7 +189,9 @@ private AuthenticateResponse mutate(AuthenticateResponse response) {
response.enabled(),
response.getAuthenticationRealm(),
response.getLookupRealm(),
response.getAuthenticationType()
response.getAuthenticationType(),
response.getToken(),
response.getApiKeyInfo()
);
case 5:
return new AuthenticateResponse(
Expand All @@ -189,7 +206,8 @@ private AuthenticateResponse mutate(AuthenticateResponse response) {
response.getAuthenticationRealm(),
response.getLookupRealm(),
response.getAuthenticationType(),
response.getToken()
response.getToken(),
response.getApiKeyInfo()
);
case 6:
return new AuthenticateResponse(
Expand All @@ -204,7 +222,8 @@ private AuthenticateResponse mutate(AuthenticateResponse response) {
response.getAuthenticationRealm(),
response.getLookupRealm(),
response.getAuthenticationType(),
response.getToken()
response.getToken(),
response.getApiKeyInfo()
);
case 7:
return new AuthenticateResponse(
Expand All @@ -219,7 +238,8 @@ private AuthenticateResponse mutate(AuthenticateResponse response) {
response.getAuthenticationRealm(),
new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)),
response.getAuthenticationType(),
response.getToken()
response.getToken(),
response.getApiKeyInfo()
);
case 8:
return new AuthenticateResponse(
Expand All @@ -234,7 +254,8 @@ private AuthenticateResponse mutate(AuthenticateResponse response) {
new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)),
response.getLookupRealm(),
response.getAuthenticationType(),
response.getToken()
response.getToken(),
response.getApiKeyInfo()
);
case 9:
return new AuthenticateResponse(
Expand All @@ -252,9 +273,10 @@ private AuthenticateResponse mutate(AuthenticateResponse response) {
response.getAuthenticationType(),
() -> randomFrom("realm", "api_key", "token", "anonymous", "internal")
),
response.getToken()
response.getToken(),
response.getApiKeyInfo()
);
default:
case 10:
return new AuthenticateResponse(
new User(
originalUser.getUsername(),
Expand All @@ -277,9 +299,33 @@ private AuthenticateResponse mutate(AuthenticateResponse response) {
randomValueOtherThan(response.getToken().get("type"), () -> randomAlphaOfLengthBetween(3, 8))
),
null
),
response.getApiKeyInfo()
);
case 11:
return new AuthenticateResponse(
new User(
originalUser.getUsername(),
originalUser.getRoles(),
originalUser.getMetadata(),
originalUser.getFullName(),
originalUser.getEmail()
),
response.enabled(),
response.getAuthenticationRealm(),
response.getLookupRealm(),
response.getAuthenticationType(),
response.getToken(),
response.getApiKeyInfo() == null
? new AuthenticateResponse.ApiKeyInfo(
randomAlphaOfLength(16), // mandatory
randomBoolean() ? randomAlphaOfLength(20) : null // optional
)
: null
);

default:
fail("Random number " + randomSwitchCase + " did not match any switch cases");
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ public void testFromXContent() throws IOException {
true,
new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(7)),
new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)),
"realm"
"realm",
null,
new AuthenticateResponse.ApiKeyInfo(
randomAlphaOfLength(16), // mandatory
randomBoolean() ? randomAlphaOfLength(20) : null // optional
)
);

final XContentType xContentType = randomFrom(XContentType.values());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.elasticsearch.xcontent.XContentParser;
import org.elasticsearch.xcontent.XContentType;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
import org.elasticsearch.xpack.core.security.user.User;

import java.io.IOException;
Expand Down Expand Up @@ -82,6 +83,10 @@ protected Authentication createAuthentication() {
final String lookupRealmType = randomFrom("file", "native", "ldap", "active_directory", "saml", "kerberos");
final String nodeName = randomAlphaOfLengthBetween(1, 10);
final Authentication.AuthenticationType authenticationType = randomFrom(Authentication.AuthenticationType.values());
if (Authentication.AuthenticationType.API_KEY.equals(authenticationType)) {
metadata.put(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLengthBetween(1, 10));
metadata.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : randomAlphaOfLengthBetween(1, 10));
}
return new Authentication(
new User(username, roles, fullName, email, metadata, true),
new Authentication.RealmRef(authenticationRealmName, authenticationRealmType, nodeName),
Expand Down Expand Up @@ -113,12 +118,22 @@ AuthenticateResponse createServerAuthenticationResponse(Authentication authentic
? authentication.getAuthenticatedBy().getType()
: authentication.getLookedUpBy().getType()
);
final AuthenticateResponse.ApiKeyInfo apiKeyInfo;
if (Authentication.AuthenticationType.API_KEY.equals(authentication.getAuthenticationType())) {
final String apiKeyId = (String) authentication.getMetadata().get(AuthenticationField.API_KEY_ID_KEY); // mandatory
final String apiKeyName = (String) authentication.getMetadata().get(AuthenticationField.API_KEY_NAME_KEY); // optional
apiKeyInfo = new AuthenticateResponse.ApiKeyInfo(apiKeyId, apiKeyName);
} else {
apiKeyInfo = null;
}
return new AuthenticateResponse(
cUser,
user.enabled(),
authenticatedBy,
lookedUpBy,
authentication.getAuthenticationType().toString().toLowerCase(Locale.ROOT)
authentication.getAuthenticationType().toString().toLowerCase(Locale.ROOT),
null,
apiKeyInfo
);
}
}
Loading