diff --git a/include/istio/utils/attribute_names.h b/include/istio/utils/attribute_names.h index 462f957ed46..ed93247efd6 100644 --- a/include/istio/utils/attribute_names.h +++ b/include/istio/utils/attribute_names.h @@ -87,6 +87,7 @@ struct AttributeName { // Authentication attributes static const char kRequestAuthPrincipal[]; static const char kRequestAuthAudiences[]; + static const char kRequestAuthGroups[]; static const char kRequestAuthPresenter[]; static const char kRequestAuthClaims[]; static const char kRequestAuthRawClaims[]; diff --git a/src/envoy/http/authn/authn_utils.cc b/src/envoy/http/authn/authn_utils.cc index 78c232e3108..76ea0365e73 100644 --- a/src/envoy/http/authn/authn_utils.cc +++ b/src/envoy/http/authn/authn_utils.cc @@ -25,6 +25,9 @@ namespace { // The JWT audience key name static const std::string kJwtAudienceKey = "aud"; +// The JWT groups key name +static const std::string kJwtGroupsKey = "groups"; + // Extract JWT audience into the JwtPayload. // This function should to be called after the claims are extracted. void ExtractJwtAudience( @@ -48,6 +51,30 @@ void ExtractJwtAudience( // Not convertable to string array } } + +// Extract JWT groups into the JwtPayload. +// This function should to be called after the claims are extracted. +void ExtractJwtGroups( + const Envoy::Json::Object& obj, + const ::google::protobuf::Map< ::std::string, ::std::string>& claims, + istio::authn::JwtPayload* payload) { + const std::string& key = kJwtGroupsKey; + // "groups" can be either string array or string. + // First, try as string + if (claims.count(key) > 0) { + payload->add_groups(claims.at(key)); + return; + } + // Next, try as string array + try { + std::vector group_vector = obj.getStringArray(key); + for (const std::string group : group_vector) { + payload->add_groups(group); + } + } catch (Json::Exception& e) { + // Not convertable to string array + } +} }; // namespace // Retrieve the JwtPayload from the HTTP headers with the key @@ -98,6 +125,8 @@ bool AuthnUtils::GetJWTPayloadFromHeaders( // Extract audience // ExtractJwtAudience() should be called after claims are extracted. ExtractJwtAudience(*json_obj, payload->claims(), payload); + // ExtractJwtGroups() should be called after claims are extracted. + ExtractJwtGroups(*json_obj, payload->claims(), payload); // Build user if (claims->count("iss") > 0 && claims->count("sub") > 0) { payload->set_user((*claims)["iss"] + "/" + (*claims)["sub"]); diff --git a/src/envoy/utils/authn.cc b/src/envoy/utils/authn.cc index a9b03bd67df..697b3fff129 100644 --- a/src/envoy/utils/authn.cc +++ b/src/envoy/utils/authn.cc @@ -74,6 +74,15 @@ void Authentication::SaveAuthAttributesToStruct( setKeyValue(data, istio::utils::AttributeName::kRequestAuthAudiences, origin.audiences(0)); } + if (!origin.groups().empty()) { + ::google::protobuf::ListValue* value; + value = (*data.mutable_fields()) + [istio::utils::AttributeName::kRequestAuthGroups] + .mutable_list_value(); + for (int i = 0; i < origin.groups().size(); i++) { + value->add_values()->set_string_value(origin.groups(i)); + } + } if (!origin.presenter().empty()) { setKeyValue(data, istio::utils::AttributeName::kRequestAuthPresenter, origin.presenter()); diff --git a/src/envoy/utils/authn_test.cc b/src/envoy/utils/authn_test.cc index 4e75067d615..1fd142641dd 100644 --- a/src/envoy/utils/authn_test.cc +++ b/src/envoy/utils/authn_test.cc @@ -69,6 +69,8 @@ TEST_F(AuthenticationTest, SaveAuthAttributesToStruct) { origin->add_audiences("audiences0"); origin->add_audiences("audiences1"); origin->set_presenter("presenter"); + origin->add_groups("group1"); + origin->add_groups("group2"); auto claim = origin->mutable_claims(); (*claim)["key1"] = "value1"; (*claim)["key2"] = "value2"; @@ -92,6 +94,18 @@ TEST_F(AuthenticationTest, SaveAuthAttributesToStruct) { .at(istio::utils::AttributeName::kRequestAuthAudiences) .string_value(), "audiences0"); + EXPECT_EQ(data.fields() + .at(istio::utils::AttributeName::kRequestAuthGroups) + .list_value() + .values(0) + .string_value(), + "group1"); + EXPECT_EQ(data.fields() + .at(istio::utils::AttributeName::kRequestAuthGroups) + .list_value() + .values(1) + .string_value(), + "group2"); EXPECT_EQ(data.fields() .at(istio::utils::AttributeName::kRequestAuthPresenter) .string_value(), diff --git a/src/istio/authn/context.proto b/src/istio/authn/context.proto index 5ea3fdb67d5..b9537ee8490 100644 --- a/src/istio/authn/context.proto +++ b/src/istio/authn/context.proto @@ -40,6 +40,10 @@ message JwtPayload { // object (map) to access other claims that not cover with the string claims // map above. string raw_claims = 6; + + // The groups claim in the JWT. + // Example: [‘group1’, ‘group2’] + repeated string groups = 7; } // Container to hold authenticated attributes from X509 (mTLS). diff --git a/src/istio/utils/attribute_names.cc b/src/istio/utils/attribute_names.cc index 5a701de1171..5390732fbbb 100644 --- a/src/istio/utils/attribute_names.cc +++ b/src/istio/utils/attribute_names.cc @@ -82,6 +82,7 @@ const char AttributeName::kQuotaCacheHit[] = "quota.cache_hit"; // Authentication attributes const char AttributeName::kRequestAuthPrincipal[] = "request.auth.principal"; const char AttributeName::kRequestAuthAudiences[] = "request.auth.audiences"; +const char AttributeName::kRequestAuthGroups[] = "request.auth.groups"; const char AttributeName::kRequestAuthPresenter[] = "request.auth.presenter"; const char AttributeName::kRequestAuthClaims[] = "request.auth.claims"; const char AttributeName::kRequestAuthRawClaims[] = "request.auth.raw_claims";