68
68
import static org .elasticsearch .xpack .core .security .authc .Authentication .RealmRef .newServiceAccountRealmRef ;
69
69
import static org .elasticsearch .xpack .core .security .authc .AuthenticationField .ANONYMOUS_REALM_NAME ;
70
70
import static org .elasticsearch .xpack .core .security .authc .AuthenticationField .ANONYMOUS_REALM_TYPE ;
71
+ import static org .elasticsearch .xpack .core .security .authc .AuthenticationField .API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY ;
71
72
import static org .elasticsearch .xpack .core .security .authc .AuthenticationField .API_KEY_REALM_NAME ;
72
73
import static org .elasticsearch .xpack .core .security .authc .AuthenticationField .API_KEY_REALM_TYPE ;
74
+ import static org .elasticsearch .xpack .core .security .authc .AuthenticationField .API_KEY_ROLE_DESCRIPTORS_KEY ;
73
75
import static org .elasticsearch .xpack .core .security .authc .AuthenticationField .ATTACH_REALM_NAME ;
74
76
import static org .elasticsearch .xpack .core .security .authc .AuthenticationField .ATTACH_REALM_TYPE ;
75
77
import static org .elasticsearch .xpack .core .security .authc .AuthenticationField .CLOUD_API_KEY_REALM_NAME ;
@@ -569,6 +571,11 @@ public boolean supportsRunAs(@Nullable AnonymousUser anonymousUser) {
569
571
return false ;
570
572
}
571
573
574
+ // We may allow cloud API keys to run-as in the future, but for now there is no requirement
575
+ if (isCloudApiKey ()) {
576
+ return false ;
577
+ }
578
+
572
579
// There is no reason for internal users to run-as. This check prevents either internal user itself
573
580
// or a token created for it (though no such thing in current code) to run-as.
574
581
if (getEffectiveSubject ().getUser () instanceof InternalUser ) {
@@ -748,14 +755,15 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
748
755
*/
749
756
public void toXContentFragment (XContentBuilder builder ) throws IOException {
750
757
final User user = effectiveSubject .getUser ();
758
+ final Map <String , Object > metadata = getAuthenticatingSubject ().getMetadata ();
751
759
builder .field (User .Fields .USERNAME .getPreferredName (), user .principal ());
752
760
builder .array (User .Fields .ROLES .getPreferredName (), user .roles ());
753
761
builder .field (User .Fields .FULL_NAME .getPreferredName (), user .fullName ());
754
762
builder .field (User .Fields .EMAIL .getPreferredName (), user .email ());
755
763
if (isServiceAccount ()) {
756
- final String tokenName = (String ) getAuthenticatingSubject (). getMetadata () .get (ServiceAccountSettings .TOKEN_NAME_FIELD );
764
+ final String tokenName = (String ) metadata .get (ServiceAccountSettings .TOKEN_NAME_FIELD );
757
765
assert tokenName != null : "token name cannot be null" ;
758
- final String tokenSource = (String ) getAuthenticatingSubject (). getMetadata () .get (ServiceAccountSettings .TOKEN_SOURCE_FIELD );
766
+ final String tokenSource = (String ) metadata .get (ServiceAccountSettings .TOKEN_SOURCE_FIELD );
759
767
assert tokenSource != null : "token source cannot be null" ;
760
768
builder .field (
761
769
User .Fields .TOKEN .getPreferredName (),
@@ -790,16 +798,31 @@ public void toXContentFragment(XContentBuilder builder) throws IOException {
790
798
}
791
799
builder .endObject ();
792
800
builder .field (User .Fields .AUTHENTICATION_TYPE .getPreferredName (), getAuthenticationType ().name ().toLowerCase (Locale .ROOT ));
801
+
793
802
if (isApiKey () || isCrossClusterAccess ()) {
794
- final String apiKeyId = (String ) getAuthenticatingSubject ().getMetadata ().get (AuthenticationField .API_KEY_ID_KEY );
795
- final String apiKeyName = (String ) getAuthenticatingSubject ().getMetadata ().get (AuthenticationField .API_KEY_NAME_KEY );
796
- if (apiKeyName == null ) {
797
- builder .field ("api_key" , Map .of ("id" , apiKeyId ));
798
- } else {
799
- builder .field ("api_key" , Map .of ("id" , apiKeyId , "name" , apiKeyName ));
803
+ final String apiKeyId = (String ) metadata .get (AuthenticationField .API_KEY_ID_KEY );
804
+ final String apiKeyName = (String ) metadata .get (AuthenticationField .API_KEY_NAME_KEY );
805
+ final Map <String , Object > apiKeyField = new HashMap <>();
806
+ apiKeyField .put ("id" , apiKeyId );
807
+ if (apiKeyName != null ) {
808
+ apiKeyField .put ("name" , apiKeyName );
809
+ }
810
+ apiKeyField .put ("managed_by" , CredentialManagedBy .ELASTICSEARCH .getDisplayName ());
811
+ builder .field ("api_key" , Collections .unmodifiableMap (apiKeyField ));
812
+
813
+ } else if (isCloudApiKey ()) {
814
+ final String apiKeyId = user .principal ();
815
+ final String apiKeyName = (String ) user .metadata ().get (AuthenticationField .API_KEY_NAME_KEY );
816
+ final boolean internal = (boolean ) user .metadata ().get (AuthenticationField .API_KEY_INTERNAL_KEY );
817
+ final Map <String , Object > apiKeyField = new HashMap <>();
818
+ apiKeyField .put ("id" , apiKeyId );
819
+ if (apiKeyName != null ) {
820
+ apiKeyField .put ("name" , apiKeyName );
800
821
}
822
+ apiKeyField .put ("internal" , internal );
823
+ apiKeyField .put ("managed_by" , CredentialManagedBy .CLOUD .getDisplayName ());
824
+ builder .field ("api_key" , Collections .unmodifiableMap (apiKeyField ));
801
825
}
802
- // TODO cloud API key fields such as managed_by
803
826
}
804
827
805
828
public static Authentication getAuthenticationFromCrossClusterAccessMetadata (Authentication authentication ) {
@@ -924,10 +947,11 @@ private void checkConsistencyForApiKeyAuthenticationType() {
924
947
Strings .format ("API key authentication cannot have realm type [%s]" , authenticatingRealm .type )
925
948
);
926
949
}
927
- if (authenticatingRealm . isCloudApiKeyRealm () ) {
928
- // TODO consistency check for cloud API keys
950
+ if (authenticatingSubject . getType () == Subject . Type . CLOUD_API_KEY ) {
951
+ checkConsistencyForCloudApiKeyAuthenticatingSubject ( "Cloud API key" );
929
952
return ;
930
953
}
954
+
931
955
checkConsistencyForApiKeyAuthenticatingSubject ("API key" );
932
956
if (Subject .Type .CROSS_CLUSTER_ACCESS == authenticatingSubject .getType ()) {
933
957
if (authenticatingSubject .getMetadata ().get (CROSS_CLUSTER_ACCESS_AUTHENTICATION_KEY ) == null ) {
@@ -1021,6 +1045,18 @@ private void checkConsistencyForApiKeyAuthenticatingSubject(String prefixMessage
1021
1045
}
1022
1046
}
1023
1047
1048
+ private void checkConsistencyForCloudApiKeyAuthenticatingSubject (String prefixMessage ) {
1049
+ final RealmRef authenticatingRealm = authenticatingSubject .getRealm ();
1050
+ checkNoDomain (authenticatingRealm , prefixMessage );
1051
+ checkNoInternalUser (authenticatingSubject , prefixMessage );
1052
+ checkNoRunAs (this , prefixMessage );
1053
+ if (authenticatingSubject .getMetadata ().get (CROSS_CLUSTER_ACCESS_ROLE_DESCRIPTORS_KEY ) != null
1054
+ || authenticatingSubject .getMetadata ().get (API_KEY_ROLE_DESCRIPTORS_KEY ) != null
1055
+ || authenticatingSubject .getMetadata ().get (API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY ) != null ) {
1056
+ throw new IllegalArgumentException (prefixMessage + " authentication cannot contain a role descriptors metadata field" );
1057
+ }
1058
+ }
1059
+
1024
1060
private static void checkNoInternalUser (Subject subject , String prefixMessage ) {
1025
1061
if (subject .getUser () instanceof InternalUser ) {
1026
1062
throw new IllegalArgumentException (
@@ -1057,7 +1093,8 @@ private static boolean hasSyntheticRealmNameOrType(@Nullable RealmRef realmRef)
1057
1093
ANONYMOUS_REALM_NAME ,
1058
1094
FALLBACK_REALM_NAME ,
1059
1095
ATTACH_REALM_NAME ,
1060
- CROSS_CLUSTER_ACCESS_REALM_NAME
1096
+ CROSS_CLUSTER_ACCESS_REALM_NAME ,
1097
+ CLOUD_API_KEY_REALM_NAME
1061
1098
).contains (realmRef .getName ())) {
1062
1099
return true ;
1063
1100
}
@@ -1067,7 +1104,8 @@ private static boolean hasSyntheticRealmNameOrType(@Nullable RealmRef realmRef)
1067
1104
ANONYMOUS_REALM_TYPE ,
1068
1105
FALLBACK_REALM_TYPE ,
1069
1106
ATTACH_REALM_TYPE ,
1070
- CROSS_CLUSTER_ACCESS_REALM_TYPE
1107
+ CROSS_CLUSTER_ACCESS_REALM_TYPE ,
1108
+ CLOUD_API_KEY_REALM_TYPE
1071
1109
).contains (realmRef .getType ())) {
1072
1110
return true ;
1073
1111
}
@@ -1649,6 +1687,20 @@ public enum AuthenticationType {
1649
1687
INTERNAL
1650
1688
}
1651
1689
1690
+ /**
1691
+ * Indicates if credentials are managed by Elasticsearch or by the Cloud.
1692
+ */
1693
+ public enum CredentialManagedBy {
1694
+
1695
+ CLOUD ,
1696
+
1697
+ ELASTICSEARCH ;
1698
+
1699
+ public String getDisplayName () {
1700
+ return name ().toLowerCase (Locale .ROOT );
1701
+ }
1702
+ }
1703
+
1652
1704
public static class AuthenticationSerializationHelper {
1653
1705
1654
1706
private AuthenticationSerializationHelper () {}
0 commit comments