@@ -48,6 +48,7 @@ using ::testing::Contains;
48
48
using ::testing::ElementsAre;
49
49
using ::testing::HasSubstr;
50
50
using ::testing::MatcherCast;
51
+ using ::testing::Optional;
51
52
using ::testing::Pair;
52
53
using ::testing::Property;
53
54
using ::testing::ResultOf;
@@ -304,6 +305,27 @@ TEST(ExternalAccount, ParseWithImpersonationDefaultLifetimeSuccess) {
304
305
std::chrono::seconds (3600 ));
305
306
}
306
307
308
+ TEST (ExternalAccount, ParseUserProjectSuccess) {
309
+ auto const configuration = nlohmann::json{
310
+ {" type" , " external_account" },
311
+ {" audience" , " test-audience" },
312
+ {" subject_token_type" , " test-subject-token-type" },
313
+ {" token_url" , " test-token-url" },
314
+ {" credential_source" , nlohmann::json{{" file" , " /dev/null-test-only" }}},
315
+ {" workforce_pool_user_project" , " project-id-or-name" },
316
+ };
317
+ auto ec = internal::ErrorContext (
318
+ {{" program" , " test" }, {" full-configuration" , configuration.dump ()}});
319
+ auto const actual =
320
+ ParseExternalAccountConfiguration (configuration.dump (), ec);
321
+ ASSERT_STATUS_OK (actual);
322
+ EXPECT_EQ (actual->audience , " test-audience" );
323
+ EXPECT_EQ (actual->subject_token_type , " test-subject-token-type" );
324
+ EXPECT_EQ (actual->token_url , " test-token-url" );
325
+ EXPECT_THAT (actual->workforce_pool_user_project ,
326
+ Optional (std::string (" project-id-or-name" )));
327
+ }
328
+
307
329
TEST (ExternalAccount, ParseNotJson) {
308
330
auto const configuration = std::string{" not-json" };
309
331
auto ec = internal::ErrorContext (
@@ -657,7 +679,8 @@ TEST(ExternalAccount, Working) {
657
679
auto const info =
658
680
ExternalAccountInfo{" test-audience" , " test-subject-token-type" ,
659
681
test_url, mock_source,
660
- absl::nullopt, {}};
682
+ absl::nullopt, {},
683
+ absl::nullopt};
661
684
662
685
MockClientFactory client_factory;
663
686
EXPECT_CALL (client_factory, Call (make_expected_options ())).WillOnce ([&]() {
@@ -689,6 +712,58 @@ TEST(ExternalAccount, Working) {
689
712
EXPECT_EQ (access_token->token , expected_access_token);
690
713
}
691
714
715
+ TEST (ExternalAccount, WorkingWorkforceIdentity) {
716
+ auto const test_url = std::string{" https://sts.example.com/" };
717
+ auto const expected_access_token = std::string{" test-access-token" };
718
+ auto const expected_expires_in = std::chrono::seconds (3456 );
719
+ auto const json_response = nlohmann::json{
720
+ {" access_token" , expected_access_token},
721
+ {" expires_in" , expected_expires_in.count ()},
722
+ {" issued_token_type" , " urn:ietf:params:oauth:token-type:access_token" },
723
+ {" token_type" , " Bearer" },
724
+ };
725
+ auto mock_source = [](HttpClientFactory const &, Options const &) {
726
+ return make_status_or (internal::SubjectToken{" test-subject-token" });
727
+ };
728
+ auto const info = ExternalAccountInfo{" test-audience" ,
729
+ " test-subject-token-type" ,
730
+ test_url,
731
+ mock_source,
732
+ absl::nullopt,
733
+ {},
734
+ " project-id-or-name" };
735
+
736
+ MockClientFactory client_factory;
737
+ EXPECT_CALL (client_factory, Call (make_expected_options ())).WillOnce ([&]() {
738
+ auto mock = std::make_unique<MockRestClient>();
739
+ auto expected_request = make_expected_token_exchange_request (test_url);
740
+ auto expected_payload =
741
+ MatcherCast<FormDataType const &>(UnorderedElementsAre (
742
+ Pair (" grant_type" ,
743
+ " urn:ietf:params:oauth:grant-type:token-exchange" ),
744
+ Pair (" requested_token_type" ,
745
+ " urn:ietf:params:oauth:token-type:access_token" ),
746
+ Pair (" scope" , " https://www.googleapis.com/auth/cloud-platform" ),
747
+ Pair (" audience" , " test-audience" ),
748
+ Pair (" subject_token_type" , " test-subject-token-type" ),
749
+ Pair (" subject_token" , " test-subject-token" ),
750
+ Pair (" options" , R"( {"userProject": "project-id-or-name"})" )));
751
+ EXPECT_CALL (*mock, Post (_, expected_request, expected_payload))
752
+ .WillOnce (
753
+ Return (ByMove (MakeMockResponseSuccess (json_response.dump ()))));
754
+ return mock;
755
+ });
756
+
757
+ auto credentials =
758
+ ExternalAccountCredentials (info, client_factory.AsStdFunction (),
759
+ Options{}.set <TestOnlyOption>(" test-option" ));
760
+ auto const now = std::chrono::system_clock::now ();
761
+ auto access_token = credentials.GetToken (now);
762
+ ASSERT_STATUS_OK (access_token);
763
+ EXPECT_EQ (access_token->expiration , now + expected_expires_in);
764
+ EXPECT_EQ (access_token->token , expected_access_token);
765
+ }
766
+
692
767
TEST (ExternalAccount, WorkingWithImpersonation) {
693
768
auto const sts_test_url = std::string{" https://sts.example.com/" };
694
769
auto const sts_access_token = std::string{" test-sts-access-token" };
@@ -727,7 +802,8 @@ TEST(ExternalAccount, WorkingWithImpersonation) {
727
802
mock_source,
728
803
ExternalAccountImpersonationConfig{
729
804
impersonate_test_url, impersonate_test_lifetime},
730
- {}};
805
+ {},
806
+ absl::nullopt};
731
807
732
808
auto sts_client = [&] {
733
809
auto expected_sts_request = Property (&RestRequest::path, sts_test_url);
@@ -798,7 +874,8 @@ TEST(ExternalAccount, HandleHttpError) {
798
874
auto const info =
799
875
ExternalAccountInfo{" test-audience" , " test-subject-token-type" ,
800
876
test_url, mock_source,
801
- absl::nullopt, {}};
877
+ absl::nullopt, {},
878
+ absl::nullopt};
802
879
MockClientFactory client_factory;
803
880
EXPECT_CALL (client_factory, Call).WillOnce ([&]() {
804
881
auto mock = std::make_unique<MockRestClient>();
@@ -836,7 +913,8 @@ TEST(ExternalAccount, HandleHttpPartialError) {
836
913
auto const info =
837
914
ExternalAccountInfo{" test-audience" , " test-subject-token-type" ,
838
915
test_url, mock_source,
839
- absl::nullopt, {}};
916
+ absl::nullopt, {},
917
+ absl::nullopt};
840
918
MockClientFactory client_factory;
841
919
EXPECT_CALL (client_factory, Call).WillOnce ([&]() {
842
920
auto mock = std::make_unique<MockRestClient>();
@@ -875,7 +953,8 @@ TEST(ExternalAccount, HandleNotJson) {
875
953
auto const info =
876
954
ExternalAccountInfo{" test-audience" , " test-subject-token-type" ,
877
955
test_url, mock_source,
878
- absl::nullopt, {}};
956
+ absl::nullopt, {},
957
+ absl::nullopt};
879
958
MockClientFactory client_factory;
880
959
EXPECT_CALL (client_factory, Call).WillOnce ([&]() {
881
960
auto mock = std::make_unique<MockRestClient>();
@@ -914,7 +993,8 @@ TEST(ExternalAccount, HandleNotJsonObject) {
914
993
auto const info =
915
994
ExternalAccountInfo{" test-audience" , " test-subject-token-type" ,
916
995
test_url, mock_source,
917
- absl::nullopt, {}};
996
+ absl::nullopt, {},
997
+ absl::nullopt};
918
998
MockClientFactory client_factory;
919
999
EXPECT_CALL (client_factory, Call).WillOnce ([&]() {
920
1000
auto mock = std::make_unique<MockRestClient>();
@@ -959,7 +1039,8 @@ TEST(ExternalAccount, MissingToken) {
959
1039
auto const info =
960
1040
ExternalAccountInfo{" test-audience" , " test-subject-token-type" ,
961
1041
test_url, mock_source,
962
- absl::nullopt, {}};
1042
+ absl::nullopt, {},
1043
+ absl::nullopt};
963
1044
MockClientFactory client_factory;
964
1045
EXPECT_CALL (client_factory, Call).WillOnce ([&]() {
965
1046
auto mock = std::make_unique<MockRestClient>();
@@ -993,7 +1074,8 @@ TEST(ExternalAccount, MissingIssuedTokenType) {
993
1074
auto const info =
994
1075
ExternalAccountInfo{" test-audience" , " test-subject-token-type" ,
995
1076
test_url, mock_source,
996
- absl::nullopt, {}};
1077
+ absl::nullopt, {},
1078
+ absl::nullopt};
997
1079
MockClientFactory client_factory;
998
1080
EXPECT_CALL (client_factory, Call).WillOnce ([&]() {
999
1081
auto mock = std::make_unique<MockRestClient>();
@@ -1027,7 +1109,8 @@ TEST(ExternalAccount, MissingTokenType) {
1027
1109
auto const info =
1028
1110
ExternalAccountInfo{" test-audience" , " test-subject-token-type" ,
1029
1111
test_url, mock_source,
1030
- absl::nullopt, {}};
1112
+ absl::nullopt, {},
1113
+ absl::nullopt};
1031
1114
MockClientFactory client_factory;
1032
1115
EXPECT_CALL (client_factory, Call).WillOnce ([&]() {
1033
1116
auto mock = std::make_unique<MockRestClient>();
@@ -1061,7 +1144,8 @@ TEST(ExternalAccount, InvalidIssuedTokenType) {
1061
1144
auto const info =
1062
1145
ExternalAccountInfo{" test-audience" , " test-subject-token-type" ,
1063
1146
test_url, mock_source,
1064
- absl::nullopt, {}};
1147
+ absl::nullopt, {},
1148
+ absl::nullopt};
1065
1149
MockClientFactory client_factory;
1066
1150
EXPECT_CALL (client_factory, Call).WillOnce ([&]() {
1067
1151
auto mock = std::make_unique<MockRestClient>();
@@ -1097,7 +1181,8 @@ TEST(ExternalAccount, InvalidTokenType) {
1097
1181
auto const info =
1098
1182
ExternalAccountInfo{" test-audience" , " test-subject-token-type" ,
1099
1183
test_url, mock_source,
1100
- absl::nullopt, {}};
1184
+ absl::nullopt, {},
1185
+ absl::nullopt};
1101
1186
MockClientFactory client_factory;
1102
1187
EXPECT_CALL (client_factory, Call).WillOnce ([&]() {
1103
1188
auto mock = std::make_unique<MockRestClient>();
@@ -1134,7 +1219,8 @@ TEST(ExternalAccount, MissingExpiresIn) {
1134
1219
auto const info =
1135
1220
ExternalAccountInfo{" test-audience" , " test-subject-token-type" ,
1136
1221
test_url, mock_source,
1137
- absl::nullopt, {}};
1222
+ absl::nullopt, {},
1223
+ absl::nullopt};
1138
1224
MockClientFactory client_factory;
1139
1225
EXPECT_CALL (client_factory, Call).WillOnce ([&]() {
1140
1226
auto mock = std::make_unique<MockRestClient>();
@@ -1169,7 +1255,8 @@ TEST(ExternalAccount, InvalidExpiresIn) {
1169
1255
auto const info =
1170
1256
ExternalAccountInfo{" test-audience" , " test-subject-token-type" ,
1171
1257
test_url, mock_source,
1172
- absl::nullopt, {}};
1258
+ absl::nullopt, {},
1259
+ absl::nullopt};
1173
1260
MockClientFactory client_factory;
1174
1261
EXPECT_CALL (client_factory, Call).WillOnce ([&]() {
1175
1262
auto mock = std::make_unique<MockRestClient>();
0 commit comments